Orderadmin/css/cf7-customizer-admin-global.css000064400000000335147600046700014350 0ustar00.cf7cstmzr-submenu-item { } .cf7cstmzr-submenu-item:before { content: '\21B3'; padding: 0 5px; } .cf7cstmzr-tutorial-section { max-width: 800px; } .cf7cstmzr-tutorial-section .wp-video { margin: auto; }admin/css/cf7-customizer-admin.css000064400000045132147600046700013116 0ustar00/** * All of the CSS for your admin-specific functionality should be * included in this file. */ @media screen and (max-width: 782px) { .cf7cstmzr-body #wpbody-content{ padding-bottom: 10px; } } #cf7cstmzr-main-container { overflow: hidden; } #cf7cstmzr-main-container.fw { position: fixed; top: 0; bottom: 0; left: 0; right: 0; z-index: 500000; background-color: #f1f1f1; overflow: auto; } .media-modal { z-index: 500010!important; } #cf7cstmzr-main-container.fw .cf7cstmzr-row { max-height: 100%; } #cf7cstmzr-main-container.fw .cf7cstmzr-col { max-height: 100%; height: 100%; } #cf7cstmzr-main-container.fw .cf7cstmzr-settings-item-body { max-height: 100%; overflow: scroll; } .cf7cstmzr-preview-control .dashicons { display: inline-block; width: 28px; height: 28px; line-height: 28px; font-size: 24px; cursor: pointer; } .cf7cstmzr-preview-control .dashicons:hover { color: #0085ba; } @media screen and (max-width: 1399px) { .cf7cstmzr-preview-control .dashicons.dashicons-tablet { display: none; } } /* Mobile */ @media screen and (max-width: 599px) { .cf7cstmzr-preview-control { display: none; } } /* Mobile */ @media screen and (min-width: 599px) { } /* Mobile large */ @media screen and (max-width: 960px) { .only-desktop { display: none!important; } } /* Mobile large */ @media screen and (min-width: 961px) { .only-mobile { display: none; } .cf7cstmzr-row { display: -ms-flexbox; display: flex; -ms-flex-wrap: wrap; flex-wrap: wrap; } .cf7cstmzr-col { flex: 0 0 320px; max-width: 320px; } .cf7cstmzr-col-wide { -ms-flex-preferred-size: 0; flex-basis: 0; -ms-flex-positive: 1; flex-grow: 1; max-width: 100%; padding-right: 1px; padding-left: 6px; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-control, .cf7cstmzr-settings-menu .cf7cstmzr-settings-item { padding: 0; } } /* Tablet */ @media screen and (min-width: 1199px) { .cf7cstmzr-row { display: -ms-flexbox; display: flex; -ms-flex-wrap: wrap; flex-wrap: wrap; } .cf7cstmzr-col { flex: 0 0 350px; max-width: 350px; } .cf7cstmzr-col-wide { -ms-flex-preferred-size: 0; flex-basis: 0; -ms-flex-positive: 1; flex-grow: 1; max-width: 100%; } } #cf7cstmzr-settings-container-header-title { position: relative; } #cf7cstmzr-settings-container-header-title .dashicons { font-size: 28px; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); cursor: pointer; width: 28px; height: 28px; text-align: center; line-height: 28px; } #cf7cstmzr-settings-container-header-title .dashicons:hover { color: #0085ba; } #cf7cstmzr-preview-container-header-title { position: relative; } #cf7cstmzr-preview-container-header-title .cf7cstmzr-preview-control-icons { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 28px; } #cf7cstmzr-preview-container-header-title .cf7cstmzr-preview-control-icons .dashicons { font-size: 28px; cursor: pointer; width: 28px; height: 28px; text-align: center; line-height: 28px; } #cf7cstmzr-preview-container-header-title .cf7cstmzr-preview-control-icons .dashicons:hover { color: #0085ba; } #select-form-container { padding: 0 10px 0 10px; } .cf7cstmzr-col-wide #select-form-container { padding: 0; } .cf7cstmzr-settings-menu {} .cf7cstmzr-settings-menu .cf7cstmzr-settings-item { text-align: left; } .cf7cstmzr-settings-control-inner { padding: 10px; background-color: #fff; margin-bottom: 2px; } .cf7cstmzr-settings-control-inner h4 { margin-top:0; margin-bottom:10px; font-size:17px; } .cf7cstmzr-settings-control-inner .cf7cstmzr-settings-control-item { padding-top: 10px; } .cf7cstmzr-settings-control-inner .cf7cstmzr-settings-control-item:first-child { padding-top: 0; } .cf7cstmzr-settings-control-inner .cf7cstmzr-settings-control-item h4 { font-size: 17px; margin-top: 5px; margin-bottom: 10px; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item h4.cf7cstmzr-settings-item-header { cursor: pointer; margin-top: 0; margin-bottom: 0; border-bottom: 2px solid #f1f1f1; padding: 10px; background-color: #fff; border-left: 3px solid transparent; font-size: 14px; position: relative; padding-right: 35px; } #cf7cstmzr-settings-container-body { background-color: #fff; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item h4.cf7cstmzr-settings-item-header.last-active { color: #0085ba; border-left: 3px solid #0085ba; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item h4.cf7cstmzr-settings-item-header.last-active:after { color: #444; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item h4.cf7cstmzr-settings-item-header:after { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-family: dashicons; content: "\f345"; font-size: 25px; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item.active h4.cf7cstmzr-settings-item-header:after { left: 5px; right: auto; top: 50%; content: "\f341"; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item h4.cf7cstmzr-settings-item-header:hover { color: #0085ba; border-left: 3px solid #0085ba; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item.active h4.cf7cstmzr-settings-item-header { border-left: 3px solid #0085ba; padding-left: 40px; padding-right: 10px; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item .cf7cstmzr-settings-item-body { position: fixed; bottom: -9000px; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item.active .cf7cstmzr-settings-item-body { position: static; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item.active .cf7cstmzr-settings-item-body label { display: block; font-weight: 600; margin-bottom: 5px; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item.active .cf7cstmzr-settings-item-body label.cf7cstmzr_inline_label { display: inline-block; font-weight: 400; margin-bottom: 0; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item.disabled { display: none; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item .cf7cstmzr-settings-item-body .cf7cstmzr-settings-item-body-control, .cf7cstmzr-settings-menu .cf7cstmzr-settings-item .cf7cstmzr-settings-item-body .cf7cstmzr-settings-item-body-container { padding: 10px; background-color: #fff; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item .cf7cstmzr-settings-item-body .cf7cstmzr-settings-item-body-control { margin-bottom: 2px; text-align: right; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item .cf7cstmzr-settings-item-body .cf7cstmzr-settings-item-body-container .cf7cstmzr-settings-item-body-setting { padding: 10px 5px; border-bottom: 1px solid #f1f1f1; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item .cf7cstmzr-settings-item-body .cf7cstmzr-settings-item-body-container .cf7cstmzr-settings-item-body-setting:last-child { border-bottom: none; padding-bottom: 0; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item .cf7cstmzr-settings-item-body .cf7cstmzr-settings-item-body-container .cf7cstmzr-settings-item-body-setting:first-child { padding-top: 0; } .cf7cstmzr-settings-item-body-setting-variations-main { position: relative; } .cf7cstmzr-settings-item-body-setting-variations-main .dashicons { position: absolute; right: 5px; bottom: 0; transform: translateY(-50%); width: 20px; height: 20px; text-align: center; line-height: 20px; cursor: pointer; } .cf7cstmzr-settings-item-body-setting-variations-main .dashicons:hover { color: #0085ba; } .cf7cstmzr-settings-menu .cf7cstmzr-settings-item .cf7cstmzr-settings-item-body .cf7cstmzr-settings-item-body-container .cf7cstmzr-settings-item-body-setting-variations { padding-top: 10px; } #cf7cstmzr_uploaded_image_holder img { max-width: 100px; height: auto; } #wpwrap .cf7cstmzr-form-control { display: block; width: 100%; height: 32px; padding: 4px 8px; font-size: 15px; font-weight: 400; line-height: 1.2; color: #495057; background-color: #fff; background-clip: padding-box; border: 1px solid #ced4da; border-radius: .25rem; transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; } #wpwrap .cf7cstmzr-form-control.cf7cstmzr-number { max-width: 80px; } #wpwrap .cf7cstmzr-form-control.cf7cstmzr-inline-form-control { display: inline-block; } #form-preview-container { background-color: #ededed; } #form-preview-container iframe { width: 100%; height: 100%; } #form-preview-container_inner { overflow: hidden; } .wp-core-ui .button-danger { background: #920a09; border-color: #880908 #6d0707 #6d0707; box-shadow: 0 1px 0 #6d0707; color: #fff; text-decoration: none; text-shadow: 0 -1px 1px #6d0707, 1px 0 1px #6d0707, 0 1px 1px #6d0707, -1px 0 1px #6d0707; } .wp-core-ui .button-danger.hover, .wp-core-ui .button-danger:hover, .wp-core-ui .button-danger.focus, .wp-core-ui .button-danger:focus { background: #9f0b0a; border-color: #6d0707; color: #fff; } .wp-core-ui .button-danger.focus, .wp-core-ui .button-danger:focus { box-shadow: 0 1px 0 #b20d0c, 0 0 2px 1px #d05a53; } .wp-core-ui .button-danger.active, .wp-core-ui .button-danger.active:hover, .wp-core-ui .button-danger.active:focus, .wp-core-ui .button-danger:active { background: #9F0B0A; border-color: #6d0707; box-shadow: inset 0 2px 0 #6d0707; vertical-align: top; } .wp-core-ui .button-danger[disabled], .wp-core-ui .button-danger:disabled, .wp-core-ui .button-danger-disabled, .wp-core-ui .button-danger.disabled { color: #ee7e83 !important; background: #d37074 !important; border-color: #904d4f !important; box-shadow: none !important; text-shadow: 0 -1px 0 rgba( 0, 0, 0, 0.1 ) !important; cursor: default; } .wp-core-ui .button-success { background: #0e642e; border-color: #0d602c #0c5828 #0c5828; box-shadow: 0 1px 0 #0c5828; color: #fff; text-decoration: none; text-shadow: 0 -1px 1px #0c5828, 1px 0 1px #0c5828, 0 1px 1px #0c5828, -1px 0 1px #0c5828; } .wp-core-ui .button-success.hover, .wp-core-ui .button-success:hover, .wp-core-ui .button-success.focus, .wp-core-ui .button-success:focus { background: #0f6e33; border-color: #0c5828; color: #fff; } .wp-core-ui .button-success.focus, .wp-core-ui .button-success:focus { box-shadow: 0 1px 0 #12803b, 0 0 2px 1px #41b867; } .wp-core-ui .button-success.active, .wp-core-ui .button-success.active:hover, .wp-core-ui .button-success.active:focus, .wp-core-ui .button-success:active { background: #0e642e; border-color: #0c5828; box-shadow: inset 0 2px 0 #0c5828; vertical-align: top; } .wp-core-ui .button-success[disabled], .wp-core-ui .button-success:disabled, .wp-core-ui .button-success-disabled, .wp-core-ui .button-success.disabled { color: #7ac69a !important; background: #6fb38c !important; border-color: #48755a !important; box-shadow: none !important; text-shadow: 0 -1px 0 rgba( 0, 0, 0, 0.1 ) !important; cursor: default; } .cf7cstmzr-description { margin-bottom: 10px; } .cf7cstmzr-description-togler-holder { position: relative; padding-right: 30px; } .cf7cstmzr-description-togler-holder .cf7cstmzr-description-togler { position: absolute; height: 20px; width: 20px; line-height: 20px; text-align: center; right: 0; } .cf7cstmzr-description-togler { cursor: pointer; display: inline-block; } .cf7cstmzr-description-togler:hover { color: #0085ba; } .button-icon { line-height: 20px!important; padding-right: 4px!important; padding-left: 4px!important; } .my-body-noscroll-class { overflow: hidden; } /* Required plugin styles */ .required-plugin-holder { background: #fff; margin-bottom: 15px; } .required-plugin-holder-inner { padding: 10px; } .required-plugin-holder .img-holder { width: 125px; height: 125px; margin: auto; } .required-plugin-holder .img-holder img { max-width: 100%; } .required-plugin-holder .info-holder h3 { font-size: 21px; margin-top: 15px; margin-bottom: 12px; text-decoration: none; text-align: center; } .required-plugin-holder .info-holder p { font-size: 16px; margin-top: 0; color: #777; text-align: center; } .required-plugin-holder .button-holder { text-align: center; } @media screen and (min-width: 481px) { .required-plugin-container:after { display: table; content: ' '; clear: both; } .required-plugin-holder { width: 48%; } .required-plugin-holder:last-child { float: right; } .required-plugin-holder:first-child { float: left; } } @media screen and (min-width: 661px) { .required-plugin-holder { width: 100%; float: none!important; margin-bottom: 15px; } .required-plugin-holder-inner { min-height: 125px; padding-left: 150px; padding-right: 100px; position: relative; } .required-plugin-holder .img-holder { position: absolute; left: 10px; top: 10px; } .required-plugin-holder .button-holder { position: absolute; right: 10px; top: 20px; } .required-plugin-holder .info-holder { padding: 5px 0; } .required-plugin-holder .info-holder h3 { margin-top: 0; text-align: left; } .required-plugin-holder .info-holder p { text-align: left; } } @media screen and (min-width: 961px) { .cf7cstmzr-two-one-col:after { display: table; content: ' '; clear: both; } .cf7cstmzr-two-one-col > div { width: 32%; float: left; } .cf7cstmzr-two-one-col > div:last-child { width: 66%; float: right; } .cf7cstmzr-two-one-col > div:last-child p { text-align: right; } } /* Processing spinner */ @keyframes lds-hourglass { 0% { transform: rotate(0); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } 50% { transform: rotate(900deg); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } 100% { transform: rotate(1800deg); } } .processing-holder { position: relative; } .processing-holder .processing-spinner-holder { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(240, 240, 240, 0.8); } .processing-holder .processing-spinner-holder.processing { display: block; } .processing-spinner { display: block; width: 20px; height: 20px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .processing-spinner:after { content: " "; display: block; border-radius: 50%; width: 0; height: 0; box-sizing: border-box; border: 12px solid grey; border-color: grey transparent grey transparent; animation: lds-hourglass 1.2s infinite; } #upgrade-to-pro-list { position: relative; padding-right: 110px; } #upgrade-to-pro-list .button-holder { position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 105px; } #upgrade-to-pro-list .button-holder a { display: block; margin: 5px 0; text-align: center; } #cf7cstmzr_top_right_bottom_left_container { position: relative; height: 63px; width: 63px; margin: 15px 10px; border: 2px solid rgba(0, 133, 186, 0.1); } #cf7cstmzr_top_right_bottom_left_container > div { position: absolute; width: 30px; height: 30px; border: 2px solid rgba(0, 133, 186, 0.1); } #cf7cstmzr_top_right_bottom_left_container div.cf7cstmzr_top_right { top: -2px; right: -2px; } #cf7cstmzr_top_right_bottom_left_container div.cf7cstmzr_right_bottom { bottom: -2px; right: -2px; } #cf7cstmzr_top_right_bottom_left_container div.cf7cstmzr_bottom_left { bottom: -2px; left: -2px; } #cf7cstmzr_top_right_bottom_left_container div.cf7cstmzr_left_top { top: -2px; left: -2px; } #cf7cstmzr_top_right_bottom_left_container input { position: absolute; margin: 0; padding: 0; } #cf7cstmzr_top_right_bottom_left_container input:disabled { opacity: 1!important; background: rgba(245, 245, 245, 1); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_top { top: 0; left: 50%; transform: translate(-50%, -50%); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_bottom { bottom: 0; left: 50%; transform: translate(-50%, 50%); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_right { right: 0; top: 50%; transform: translate(50%, -50%); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_left { left: 0; top: 50%; transform: translate(-50%, -50%); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_top-right { top: 0; right: 0; transform: translate(50%, -50%); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_right-bottom { bottom: 0; right: 0; transform: translate(50%, 50%); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_bottom-left { bottom: 0; left: 0; transform: translate(-50%, 50%); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_left-top { top: 0; left: 0; transform: translate(-50%, -50%); } #cf7cstmzr_top_right_bottom_left_container input.cf7cstmzr_position_center { top: 50%; left: 50%; transform: translate(-50%, -50%); } #cf7cstmzr-sticky-message { padding: 15px; position: fixed; top: 0; left: 50%; transform: translate(-50%, -120%); width: 100%; max-width: 320px; z-index: 500020!important; -webkit-box-shadow: 0px 5px 5px -5px rgba(0,0,0,0.75); -moz-box-shadow: 0px 5px 5px -5px rgba(0,0,0,0.75); box-shadow: 0px 5px 5px -5px rgba(0,0,0,0.75); transition: all 0.2s; } #cf7cstmzr-sticky-message.active { transform: translate(-50%, 0); } #cf7cstmzr-sticky-message.cf7cstmzr-success { background-color: rgba(240, 255, 244, 0.9); border: 1px solid #0e642e; border-bottom: 3px solid #0e642e; color: #0e642e; } #cf7cstmzr-sticky-message.cf7cstmzr-error { background-color: rgba(255, 232, 232, 0.9); border: 1px solid #920a09; border-bottom: 3px solid #920a09; color: #920a09; }admin/img/icon-cf7.png000064400000416724147600046700010545 0ustar00PNG  IHDR\rfiCCPICC Profile(Kaǿ(A`f{CHLeD宿شmY컻"^:f=**9R,"t%1)fm-%*t̩vOvSA\\zN=/EN1I pHYs   IDATxDKK=#2n#nۺ[u2#ʁepS.`j:Cr>υI)B> Y5'\Rk!*krS&L#"x[|[ݰ(ǃtEɌR "9 s!=*qj7bN}=^,i哻93w,i!ↀٌ&wg-'qPE9R.0D8.ZSk?sQH hr.(Ey< fO"}G0bǧa}-RBsP x*`:Ʉbʅ9 Ԍ 7Q,z3Yb\$@< .P ".)0Z[Eb Kμ,%kܼ;+fpJ G"l oj0 qdށ y͕%aM5XK$6s,xJ5匫Cs1ȩNA-\h уP PD ku\X2Tr!`g"\1&='l0x>B]A:2˹%> f c!テ$ *yy *tSf2d, J~`*[+a^ҩd2cufrȾ0 p pm &c RR ^ Q0ƅ"ڤ ~@ ʱ(gF$qߓޜ|q&MRz8`fbp{@"@L GK\EoxV;T'o0 P$Bf z5rW1!Ր{sZp)t/gy ׻S'd)=7b D*Pxs_ UAE>8\ 9}dNt88+aE0m0,\:> 0nLu;d`Yqc)!ZX֩&0]xsE)@N L>j2/U5 J\&gq3a, J*BΔ9 EGc`9y» @9+Ű,oj{1X>6)p" F\ׯ'"A})Ag8BHGM9*΍ڃ/3x_oNw|*Gҟ( S,W+m8)u<WeoA_{î9)3R!NI{t[f/"7u MwR8Z5{Zs k2/E0ZZb*7lk0DD$H;*t2!P5B:#c~#0y [I78Е)(5$R/ (! k%)1j8pLvM%Tic2{3Pr%lH n{"8>+i?_x,[TuzWͅc$AmYA[cǤ_ 7sTEb`yUߙn}!5*3qhOAWGGC`:z ;ws~54;aNƊ5>k`AB? bUFsr*,%ƽpz4MƒybP}VDI6WCGO%[!z G!x/<|>H6qmǓ5Eɉ^ 8ho羃p2ʼ PARX\i##ȿ@s$I@JJ\"LU#0r^m0VA%QF(kBQlXJJ&ASIz3DҎ ۆMTQ>jsNR:+qxfJN[!CZc%]T | 2!V#Fr%41eLDQ:{$8Dq7RK52; j!27^ FZȑ 4; Yj11 ?>.5V9'oTh@ʅE%['e8,;j5b K#g`9'#kߤȏ$ޯ͉T#w,)Ck\xdr2&o.Sb@5!Va*Z_ bk1q?6moplK$MQ?ya$J ΛW#DHU3'$9x8mLrNSYߓ9x$"1?J=s]CAkl)fK gEc{8uSRx2Nu'e&% 67e`J'y}][is9U6_XLޚ8>6"'3sk4W8) gIRL&/d'zkMrڢؚEu fH] Al8UN㚃Jz"S3@d !HT@ -g`bǤ5e!7}P0M:;Z%r,$e9jy0& š  k &[bH5z0t|'Q }#TRhS8XPꜩ8&A!ħ=hEi3z<q`(jWgF}+(ʣ|ҁ?3T! XL$J*NbiH zC=9kNhɌw"!ˈb!ѐTɿ=:#Q_c(Fc2#л@ /q|Ic9PU~sҙ0%# zoh.`$ \c(磒J!"P{KqO쌹~` r-7|"Dz_s[wk,C'+HC |QE :Qq5fRYl{oDI`!r16mu\HZļ9 $JN9Tss~s]N)B"- X`|:9Y+Șzfٝ}aٜ <3k5|TBވe@qwZ[ l>& +NM9RIDxL1燲|ul[ij IX[(JH$ЀH;ְM]w$q79=T$ *x׶ʉB C'yb\xr_/905TCFK4x(9'(k-RH%Qx>dvo5b{@FH\l3IrԄәGrv_Ms>3"reyZ#1/l9K Y s,<á|<̻ѳX<5Fvt9(uG[_1fzs!{ MPTS;à@i3 UDF1u$T mM -JݢiՃTO]ryϳi;EuUR2T~M~?ĻZ&ڷȗyBljҮ% 0"3ӉhBpb"GfL.kljf/"N:O^1eHXNR1Y%) !cj㠉L_LOn$}q }s݉`Ds$N j2b|kv1V1m 'Prftq\6j՘_wLz@H)Izm02Ƣ;\V w浘`' |9m;as e#'/JZpOx,T3Eni7H$X rN7򼹼O0\̱)G?0ioauC)[dSې)T'Fw 19 9WܝbRSERi;?;sc0mk<#`_xrɨ Uvg:+%PRb {ul<ύֲQq-N;޽Z4>'c̗R6 _ zx<9c13ZZJpF8KivӣQorK+H!&Mm #ٖx띾BB'7pa gUf{`q;vac# Gn׈mpIAAo?}&,?oEĶdfvpΐ,w޳u-rE Crѧs1{KF}|@Xp>*DZwQMhL n̯b2|P,Qj>i>x}_$"~q ̾px/RI$9 |5";,!d{l.b,"5E~V4*Xz=ͨMrJΘ.rHYеy^ opgy;GMԨܭ1mHUYsRU ™~q?)X1͝ǥa D LOް_:(  ɹW'7zg47ay}TwXۋqL&fT^u墨JZ dv:3@zf줲ݫ剥0AHOc\㨌!ctBL뢷NB=*31C(k zkg\oW$9/zƎ'')o bT_w->uubR~!KJj:kMJ*݃ >}b,< ҁ5v%#)q*\VUgg@stș*xХ,qmGSmmQ>Oꃬiǃtӏ%mEI 0_;֯Řc0w߈v޼$@S5녙avR4#z߼},XN^ө|݉c#@};v! DWa΍;mNG82:Z2Q2I|&O2ǎ--h*~rϜnoqۅ `0>{~+H93T3Ɯ%5PIi5I&T*ʾ+'"\ɒw-w`5OgBx{!A&YdؚjؙHGZ%➝<3ZϵdQQ*}X:yq"Q |Fnl}mռd;n+kQ`ź5g2zӚq \7IJZ)8 ԇ'NC|$ #9e>w)uNJ dK%/:@b!)@n!a^`\NJ# aSpn' y}P)wR|uҸ)(Gp/6hI$)ӇzSP(mUן7#[ e/'Xe+=RtwGJ?v݃x])Y#HRz{hQ.?f覕kZ1aBxGLBN8ӿnRYc'䣰"3v<8Z?Pw IvqK3Cp%geIpSo{? qOgO- d > -44*2'5в-J .4YYbW1[羌Umʉ;q&FY"(?nLRFт 's]IIYY!R2ϟyVpS ?ʐEJߤof,mt@[^`&D r!0u@/r<ڛk-hmLNdN$sm;;Zhs.ڻQF U~[Fb1F)*w8LۅtbnպXvi)u%s-,,)s^)J$/:NF-pf􄦊Q:cPM' qnS,_޵i`8Ck {u:м"8A=w6a\[4?ٍz틸;mDB*OfpQZ"Hh9~`wY7O*b0Ԕ￿6 '5()+> [, ZodM$2 #%<. <SNP#@lKTXwCę/M7'  }jjTʂ!܃%I}}y! 2Ij Ssi>8=$qG姲;_6_}iaGWCP ; o Yxs79RQ CZBPn*|1{LL6 z0ԝ@l(6߳(yuң~}|Ht)zm`ըHX`R [&AN?s98tȅwەX톙"2Ww!o U{AjeF`oHɬpss3b4hvZڸ'D%|`.\k ZinGeEюOp߼r<ӟ)3dЂL$C2TB#`߼/8IEH4S٫cxEڛ"B_ʟIn၀pS,h݉ؠX"8%`-Im}uZ=IIP|_7UXZqmhdtn;v'oєS,ֺEM!,B*h4Xuuԝۄ[/AkʟKO?F)8 qBݎX C!Bȳ1g%iL@ [6)eg-YZmؒ"?2Fm, }[g"RH3b?{1g7'pUQBY}!d}s_F M;U1 565,D] _3`R#TOȕDނOl+IBDqQ\`Mc2cҰAr9ٔa7닾Y}`mrN!y+tӶ2Кθ/3㢼OMMP3܃Qj6:P5r$%kax,/b-^4mA*19#ljE_IꛜW=2- s9NlĢcrm8$_LR*쥌a{E~dX}.g8qU["N& w7~nvSrtX+X!МIiQNJ_7m{<b]b|W@ȴb#XF$"+BJwJW.9?(f7;eJaqA$P23a#퓾&Ir;\QG82/vQ*?.875UqqIGl*vTd ס߆P@杮a`8u7F?߳*2q&I܌ zn _S|T&#jA`^p9;RVrۨDzo؀&/z//Z 6pȰSkz*Zhua Q w\_ _B7J>- M's'ZAo %x1[@"iZqR sm|Y8}]ok`+Fhم4є"=d;'ƼA2$&!whd*cK4M{6B JT'Xq|6jMv|V3.~wX&Ly6?lekb+0r9(G#a*g1}~̫xYkM*7{N6c~ܨ[XxDc^+ aC( kp|:tĕR ۂېk S*q$;;ejJqhi)Gݸ⚆NM̬\i`;#=SOM_ sLΚhģxAJAJLΥ4ߘ*Ns1j+T_`8; PpD20?A-ue (7 d03/m<5NI9SF^iqfί|c[?16qg#;N'{;H4o1*g[)ںa! cǦğ3rJ5R̽{fɘA%I0&q}E*P se.DsD :W& f#Hi3fLf^!D؝" j͌jR Ls"]<+F3IP֘I־f򑘲me,wܛ\2ogАjuRlv ĢxajLTy9JoA ,F ϣ2wf`/pxh+N9:x|g}qq(Yl_eL>0Miseyu&XSy^Ǎ7|=ZK} D#qRHDOSqpHaA.UIqv{!`)IT!b74j}0gxTz@Bw(ր#a#R$ pΗW^ o^BU#+Эt7y囱n4m܍`|Rbcczd@ RȺT+Rpql;ҔBμ `7XNe YV X_؟;8rɅMW$ܬ ίWq32v\N\w\ڃu*q~|/Cqowg-Ny(--,ϟ[\Zb%%{P, $VZh.h|dֽM4KgH)7_e~\NXhVwH"MɍǙz՜>/8/wGZPzjZW$-14*G ȺAnਉĒEO7i$)wޕq2(հIvm2C` UdH&tّ\V7"#arJ=yoG"!)N9cܘ+;{#V9CٕXq_;$gt4+cue 4*w@1hG^`&s4*5?3p|$v?_O%`Vc*dО $ ܬ!,OH{ R2DJkV|Nj/Lړ}G),#Ņ%s)$Y\1H%ޝPJG8 )w!kԭд (>'R3&Z]1c^,04 s6\mHrƲHo?|.tnȑ>. M>a냔w$ڱVHp*1eṚd5([YiO8+7 %R8+ET}lLđdvތ4HT%Sp\eRq`&(% Yor{`Ǔ4ȴN0vd$GZ;Neϸ{GYDf*V.wex#؉d=驳$Fp|xNЪIfR J/wl`]7s,4rs 's(c^$CN&F_+Qn)k&ߋb30rКl u3,)kE5`I5fw {!˱7"vLKhUIRYǙe9α)Uq5ɨƥ}rw#ix'U>М X vml;G[TXȉk!;V`_g̅YE}[yu{B\γ0%NZCa(R !Tذnh LܻOL M&ߔ䁺2 + BnUe 9{LaU<;-~j s;cM,3Cɒg$M#;)EJMP@Su36r,E `" !~)eJIw3Y1ruȆ_r .`QJO,]8[ ?~tq6F0E&3d i7^ 1orXyR#쑛0_Dg;mk>9ʁbG#WDᜑZEHς_"i?dNn s%3;AkE5_L&c 2R˘Bxazl`6趛lX8̸g2D&s:s ּ29#8kqD ZL!eP*?;P.=d''98ssBJv< iҝ&H|$c}&IBM9然ʹ:'Y1BJ7fe~kD^^H)gߎһ #8%J19ztj;y.4#Qxz'%53I~=Pu?өK٩>3g Ƌ| MSe;ow:2o؉]!J)TZ~u9T2$FnJr OY{NlzrTaՆ؂[(}3Q{5`hѰ7y7ǭb#ws0'6IfQv `C,h+<gl'6R 8Y[7F0"sfY*?θ{>󜄒}qq,E4bld;kf#(PxzNeX3-Ad+ hEoeˌMh4N {HnW~="vV0gV篎_B.4;PTbk }o4RI585Ҝ`g,BH)6Y]=%v%MtwypFnqf<'ڝZZ#GpDbϳVE -,29;V48RK@Lp-LgKVŦ\ߝ)W 'N܉R*˃T=& _|wVvLKn|xVEݍ;7VJlYh5?7J T+ӂ-Δ٩8=2YQ3#7?Oք; RY,L ֣M7s/49 u5c#;"Nvpߴ<rNng`RuŨ6;ݜ'=H2$rDJ&Y8Дx2adhg4HRl}VD$Z AWSw]Z(3t3}B5]k^vE@D2ŝ!ʽ{R8#MW~qS)I&:3/6 w<Mx voCe<JwZ,O$gLZ7wEUxh~sݓʛeDk͉{o: U(B;cj7jH-|!JlUR<Ήx@/ R* {ޤ3r%#͌Sd2G~O^vP,á4^Sc[ چkcZy=]gNν};in4dwm(ufI9{<OJnUqŢ5+zG΢շ9 ؓ&t]j}E,~֤5 ĔЙ YyL-~ 6$d\Edڛiǁnꪸ2Lr(q`KMģ6l:Rqׅ{EIuǣ(oIHչ 7Nʆ= Qd[5F,k(ڨp Gc>9?5.+H#O47Fn^j֟6 9K86ɨ7eɶLjh~}F/4=M(r6=*҅oY8Z?xccK*Փؖ_LՄ%ƾRI2lŠمR96nՅZOJlMR k/z_̩w GK_RIC3[Uޯs*qZZ+zf}Ƥ2dvcjYHWؒ%f 0m rDHlM`ν.D*q&2mDysFkϊ~,6S,b[ضeJU!RJ܍ ѷIؖE9Ԣ|tS0$1G\5-rE%RP@Ii+t֫&{x *8BDץ|[b[q;5YO]#֜yb= 5Ph dq>8RПjQdHxhGHf4~;Ä\FJ$:%]Akm|o17 2BcnDҧhX,@ReNNɑN 7 -,K̑8µ-4bs&b&;z{R)6ej6D;hlsJ!¯ENobK4ݙ:7g f> +fdY :AkdrR[r1.VSbJH4)dl5 <ϓ7_-BI;#x;E0f u1ʼn 9+'@5 Hťvr(z$gπ"# q(Ov6X'5)E}DUצoB֋A+3sEo ?Dsm[V7km`."*ɅI{u~o{co_儸A%qqbA)tVd9?=Yb/}No}_8u \ѥq? .ᭈ`7*,Dj m\C qX! mԣNPN$ yG~T vTr{ goꡓkNy rprͰHٙ8IK$%YnHfңhhRloAϝ 92ZDF}YP=\Q_x_ȳxdnZ9.R+Slsl͘_*go"I&dPu<Lb,#55h9A\כt9nVmaj]'v,|CS) *cGXޅ==ٜ=〰h]$IK/FjaT#[R>jcIѤ{lJHF3h:e pI9*{Ȋ '|xK4 rqAT]hͣUU'[sPSɨ '}.Yϰ>I灔ʞ  OihrᓧbPO 2l,ot "R L^#[ Z@MW7'Ak\I9c"TE2=Ga[ ~~3s-;!ԬLQ%5h5vLsgUV)ȭwX{ЊaBr0悖„Ê#HpµF0D^p1:JO  ̹Yp۔TC7fAw=ZRe&O0kN\@fEًM&>EE+Ȥ|$cHZ{;G[-5#G02u|]sF_eIcɶ[r*\~SBv7PR@3D Yf{8x(T3vGM`䞴VIRq2kE3Y+Vo텪G&MƜenT֜ ԈfzMj'Z83{R7a Ԝo{J8$Kތ9(O5ЎXL iRϊ Vm aΩ5j̵6wƶPQkA()5q55 'ߞ@aJ NHlpw= Gݤ0"3d!qG^dJͣݍb|%(_* D#L~ ɧ rwT-3c惦J5Xys ,t:o{dOxfDyR["`֯kED;$- 22(EmGq?qT>4pC7`F-~wZ p%~Gkл&Ba}CL7^T,$WT ϒIIn3+d_m0m#Ղosrnt.Jm5}g#{J :bKtr񕰥ܿ~qG9%tXB*35j.*2d個БeF\`Iz۸H~Ӽ;9iV {4A{/T49?q({1gJ4ek3QțBiO뛹;r"=H )9kq)0mׅփ<{wҟ^?Xúk?㏓+_ܺ(D/NL"C耯!r*po\Ɍ:]BɅf3tA, 3rRGYD_$ e4 >v=p`ha!qmTq Zuݨ*ϟGރRce/*f4W6c*(5ӒS.bxJ?`Ao3u9wG2X\G\ ꓭH z_9k,05mN/y9c#!p@9T(ߘm2ճU*(=sbߝƏ?ָ7u<Ҫ# eW`J7T7Kd|L#rS4PǽI fXlu >[8 % :jb Ѵ v0+|{#3dw`zṣZ1[?ۄB{ortR,j2+\ɩ[QVl8 sj+%^<'?c )"kc}O8`}Rs4ÌjƙkJf'N)B+rlڨ,ָ3&N:HE/3j!Lj6Gcwl1}#8{}X.+6-B3=T_dCMgJvV )c6֒SC-`uwXj뛧ݝyAMwj:94EyIcB;@y&7__'/ƞ8 Kyc4,ʞ~bs.{I7}u$D䚙ħ|hJ CT#$70cq3#?>>C=:kJ*$AVǶ'lGr߃+Q/RZ ĥM` ɏA'>.˄kkY3yCDiLLjB}؜̩H@LPjIȣ12*JJf3|ܧ-CA򎏋^ }xSFqmK$ƌFJ_|OgYo>(0Aǃ_o$9W`o&|vp㣃( %5VXlҳbΞYIx\|)U 89Qvļ;k}0xQ J"7A|RHQwtVR:KV 0m"{ ݔd}AJ:\1N'/!iHI^o3"۪o:9{zmH'`iJ$5& F=s̅V"[Z[ԪSA._^}bo$TcNzRr¯vlum7b , M*T /|z:cM%$I8hgFS F͡AvWIlʩ5t}Wcoa˅! f/(GxTనľ$'r0lJfxt@bfe0֊cTiR/|\c1=; [c%&JOl>D?iwJaq6YzUqArЀŗ+MJa})Sp9ۙS)ќ9%XOac~ nFk~PF4>z64qaBGAIp7{Ɲ JF7o[ib{1Ģ Ɛ9` 3ȇkk!9޾f}N7c9Pؓ_orS3N)֢xO2pK i/;6T'`7D(IT7Hw`eg &Ƒ S4)3ZR})stt5G:bJT );+,h2Q)Pf+G(lThHmWrN vrP9l̀?9o6'hZL]:q"c2wL#С1OD>G u6:z.LB#tK$ɠ}~uq$J*\7k^lq3"0vSJ{PrriW2+$Npp qE/mR4Yqx3;WAk1N; :( p98lu14CiOWJ"i,' =c3fH6d}%S h ֊k-\x;{_Ԓ>ZׅaX6b)mojS> 3'%/0h 2tHu >g~ T p|f-n ,7U6R*|5d9eָRaSqɶ*;F&<[θW ە'66(,)9bAZ %w;* IlgPZVSRFsJﶝ q ?S(yvSEGP *['Gs8XaEFt3"EMʅQ өkTuZ^A/CV``+@Pg{$Ur~08~<`3)5Hi1)-čGa:uޓǗ֌~njmԔ~sON­ʵrȂ}* IDATmU8TmxG9?o CM*rr$u}K u}Py&M-jdw. $JI{ CՏrR2 ;G]Z({gXR ^N;Fa8~Ls4ebr{LV9IoȹpHk2{dti)iMh#$[᫁9Cn^RD6"7ƍDRQ u+x7j M Au```9 <$1.^v㎧$.7ؽH<'}c9 .K^ itL )#{ ;`j$M)ӭS*0|%3xvwjmܛ9nkASΜ#k&J+18φ{FlKaS wOs9?)9t=uK>6¹+]Ŭj'fb;,YY3J(<7kIzRx)ǁ¸^pL<$r~t'o=8ZE`Wf͍M> JVRJG3"ZMJ-αPsn2=br|cI' _nݝ,Gt <} IJ 4rŘV3Sv0h"eP)-<,F{-Ƈ2BxT2[̈́z8c)a-ZΣ~p~}~dqQf쉝(yQl Oj0]ge\NJWf)i +V_ Vp0js~{bɰ[A5-/0+բ*Wy/ڊL>4a+/,@ڱ͙N.B_a}jgCu /0x"i"W}%EobR) :9k ڤԒI[ɏDjcǏW'K&"u(Ħ #A珓~;60oФ#~TO48@kR0 QPhp`8\P ’. ng8) 5Q hB=*3{kod/mmDDt]W'Amڊo?841σ,ks2iN‘+-7\oTs86&pKxzbă߿"/~]1\@>C>wDy9v 7#<)2pro)^6/޿B-ّ1t(=X!" / w%J;ٻ$VL.a$D!c5C;w$w;e }.4HDx`c;B+gru;Rsa5d)e; FiS+Nd6AyV۰>0Sm[ԯsQBZuٚH%sJ9k8!IZ{垼asKx2u\y:Lzꊷxqoc7$=Q(̵YèGe*ggBu?̒9d 6Yk3*ĽBZ#Փ2&ˣ>,ia!ڰq^*㤯 9dffC,2XñjXi*BV/昬A᭙)dOQ!fAVlTV%qgqTٔ1͐=N/u^Kb&a,W"enºӕȤߒ‘yORXف_[{I{ "@K]3U07r+$#moRϙ\ k^(b_.ocGT`XkSJBJ-5‰{dByGI7^%b8#2*arѬ0`MZ taԘ[VR,!DhS} -QhojkÐ$hMj~gl+Zwi+)k.pRK촨jΛj[Znc C'/© YXߟY_8ʺoƸ[P37ݡ6xKbZN&NByfdyٙf9 hX{׳PR^ c\6* k'F)Bay 3=>>(:Qr)8"4٣6RX^C4x3|`Ǧ=TKUQ^"M=c~d#yIW`"i+ Ⱥ@Ur#apEasu.~c rJl;R>ۈ.!omڃ#Zmq3%qZ>o>/1?xu_٬b|jʲët+G=Y 7: y+Y&5Ԃdq\$vg,J6Abx$t bŜq,7&s+4ԩ>c'uu $lVRZ.+|c8뚔)-#PS"025 #1HM 9!xl{r~_xfvcΘ! w0Pd}nZI ؤQZiPEa.g-,+qf擣ϯ|`t|H9+Z0ߟԠۉZ qm +{èA}(k#dw(85=r}iN_+pQ{ǩb0?AmQY&1ZL¢"Bz ɝ-I<}s&wbP4[dQlֵiu}^.dj؄c-cH6(j-b:)X#Z^|w$-<xkSG!‹RJE|-na&cd=&5!sq߀% hFT,wꁬ;&,aUCW掵s֎q z_Msl;C ^\WGub%]7Rͨ-mqES-{ᨌqq*X{80_a"!4Aj=;Ga)/r٘,JʙyO 6Åy~H<\މ탔 ()&]fw,W;~t(Dz4/i+kV3rtr lݸɶ)%jYa77ㅏAiU+o+U;i|Bi#Zab"ù~q)ڠN]j_؇$l [`XD2p|J>⡟_k ) EPbf/^lB瓚+:@6BKY2s w\!F> vYӐ xr%ະ=$!ю0C3HY91Vܘ-'?2]v'5?}s N8؍JVFPPۃ6.pf̣Y'E+HMqʨrť=* 5pxAG,#{G< 鶘4x|2_||!$l p*|,,#:g9匵vٰբ5GF浄*" 9)J̙2HANk!xHF8aD x7~+{ R2S!im#܊>:~$rK}C@H5Sge`?ov盥D:gE>#ª\#R߳r֙x|SDG(ΞJ΅?w?k  Gu{-6-; r;zFr|K hB՞rD "aR3i ~T2:&*;E|̸? {D7)Wi_7cO{"U)5#k%Z2š %x.Č+[E9׋qTz|E_oڜHkV{X3'B^f#{3Aޤ9.7ÿh}.כ1&_TL =GGxXq  69`O^s;FA3EfF-'oaXRX&:(PjVT|| &bVpXܳю`q?mW#ٲf~\K H'J]{eFL,S$fsr#֑o #+1̪08uoɼg `u~43,'E;SĎ@ά5Wge6QJ8Z$J:;yl]}~<Уq37xj"y5lJhd,,y`?2xm#K8s;G3QGfOăDPZJT  IU"7q4@xB8PqR5DGjakҫ#S@T"6. 1+ݳ83%ʛgF%1'7e[01Q)yt~Oorʄf剜vؙ1&fJδ 0JxrPD7rgZy-GlΊ%)LRqȅ5ڗ-1m !Dm"T.@?jLз [ M,Q72v|ӹ>ƤbmS~Z7"g~ >?`l*d{n;}V}PI;"H8Q~_׋kUp5]i,pv4m֙:$olOsvl8KCx1-sRcS&đY$<'f+BhI9ja{_7#2G?"ns,8W{ʍ[lP~Qr#yCFRvD׊!9{ 2tR}D O)GzQ9DcC}䣱|Rx3tw+VPߊ2)eNјQh8y?8= "̵s֞w |[M ,(8:_!hFku4 *Bt 7DMGIP`|H?gkL.(q޶ꔒ(^ 02"ATH4}pe?Z̸;gPS0onUqgEzVL+ݔ ^0;Q_3&$ϝ 0;g+,sd8#KP"XJfqӃ7v@YZ#om-ju9Mkw |e o8uV½$* bОSؾX M"Zξىl yg IDATJb$V*n7^d`σYt0ÏXI/6<?<@ׅY+0ww;s_gv$\&fE!jBv{8W*GXpYDՈ7,IEo6rJIK UH)O:kYkS[@N-7¦9t\4JiP~PB}m /4t  #'=s(%(9Y NޣsUS8$Db 44Ev:,95l/{>= PY-U͸olJY` #W ;iTN$9y T)82磡s7 5#^v-wIGa\U\;y5jc F=[9MRNͽSs})H ?iWdgCvmH6qNȗ`x.XXYRٿA'|=2ȥSp52zpy y 0SD}' 1+<WDjGUq,s{r?+ҀERb60lgj fΎ vrI{ v#j3'*ǩ>X)sq8n{[2OID0LR--tdl,d2P=1 A+~Mh {_rWeLjd(=k\1h",ua%ɜ@a^oe$eA6LiwS#ocMخ\ l|-w(oh>=~ˤ\@X*ߘ\T4{H>1E>u3P+83m2j?(QP~O.γђ )U@fx~Pϴ76:~Q)R-q :HR4yp:[ ׿WgBWq08倳(_n̳h8ίwnt)yN~Ză9%Z̷Eq ۰WeQlhttl!(-1zwA=sM1uFr0.[ +%HzNx1y bɵ˺"1,]#bIr>IHaa1ch?QBB ٍȉy( zGWfA|=%D!1Wwa#y8b!Xamd 2x9Ο?$Y`{;"=BaNR=HH9'TCq˘}scI}=xmy%#% EQDp9l9fNq4LDfi9Sk\}dyl$ |"R"L3>&*;>+۰?qƭ WGUb)=Iք쾹֛l'kD-yT4'1 %E/%ܾCb}`R̨lxI{Ѳɠ{Ąӎ'S}Öh-yRĉKH x;"mH? YNJs3HD{Zl1]B FO#T>./|'Zst]{#aH*f6ۑԬm3FooڸDwbngFZvot7>LkAIۧWSj {&EqP5c_)Ν&폊DU6d#]~U@uE>1oW\+5}:{3Rd9`Ų3܈JfWS-zhza=a΋_ Ig)ʯ+LRyvbZWdg*o>.R>pNlYINJYc0/I;̩ k!e2d QH1jYO47[c(̒d`Pפ3{ΛTؼ>#)JՕna_ZATYXJAߊ* sJE؏Z6~W(%b+VC"2$TPXrSf{"Y"t=dT%%"%ҜfQwvv&N#i>5J`VN [ aAU31Z00TІbQ7?͟[h%{b{cQ~zDN*J_0kƸQMТmaL 򘛔#(W95FNцδX!s9'&%g(hB5)ᆲ!Y*bI*G|U.k75%TU3_K5AU!p"**+Zx_oFbđg ġ枃i".7-g||$ 97>Ac1$Jw ә\K|l0twnhulnlIhqEZr xkXd4X߅7גxr;bضRH93-)˜c/RJ?o7w^k#еe\vqJu RԙV#=;XtVvQ ]EQ1Nr֗FT2A=SaxHj)mJkHj+#<{6ʸcfA ߛb SP4zO zYQrj=d^ؠ~]Sh3Zs;6L\ң][b\+n?煙@r ZRv KT 53V}ރ<6}x%/sńٝTNr6,ېM ƭ, ڭLjUdЙ(OwD/CԚ0'5۶\pwV{-6ӉuA+6n\#C= ~_GG8z8!9X%27&Q 40%1ǤfYtw/~~\#OEbxS}E;ՠZHc}6H*H9QEIGA [m'yX\S~݌b#Z⚶WKr("qOQ/㤭@hy7JC8jA^7[k,.ֽP$6a" <\yjJe`xŴ]SXtMBYZg|`)-9qw#O~`q3d VYT6P!a V<L~pdcIY؅@&K'0OBMz6Ga+*kҞZ?#oJ9ɺ7DϓcJn |4/(6i`Df_aJ>2aoS !,A .¶ gwHٓ5&N1dXj׎'hޡ1YHe*41ɚ}c;bA:ϸj*ՃlQª(hL\Ɖo.R!97U00\1Fx*>5Z=߼Nʐr ?<Gk\PPyS7tC}8pTg;br(p݋yĻ,E#ZPROw/?QZ ~Vf秢dRO/k)%P4^̾z\IPс+ {1", bZ+A>{w{ wKd=fϜɒ$wwCr5 [AНI93\[[&Պ\!p|wBCUZ{>y62/Q.%׏M %yFɀTl D6=9h\.GR ZEſΊ7L8j¬boLk]9ѳ4_ls#Rȵ,L԰wDz. J8-\?UO.YmlU^כ³JRkӷG֡T9n8)4+qcɼ Оym3n7{9 vR[s 9:f,.v4uJ~ 4bE{TT?to^+~4eSa7֕F(DQgn(P ~я_'EeǾ9CX~6dLhΨ"OMdAh*JI\Kv~L䴹]용EI⾃Wxۦw+iYa>`˹yBhk fFqѬ5+r̠H5sfgX|Yn0< F'1h_ܿf_SlFS{ݸ jt,>Vo7o  [_/rfbIm\ɹRۓgQ\o)IAʪ795~l^w03䈦Km7ki&R؉1#f#@.Ep. oW'V 0jB'Ў ݌'Yf I%kn|]J9ykL^x9}Gْ~">~ɚ"LIirLd93lLWnEy}q E tq f`nsZ$j$w&Z/ZQ)}Vc"p 0dy=Ӗb\R8rfo Z7-֑%%"*q`" OAg=-)V$ Cl"Pc <8̄9P#"6LޯI/ڱLJOᨃpP{ >Ө>_؎+= @{R#SWEvj/jY}0ZkD = Dw:aµ:hx;W$mffO-_8K[y-p~xo τ7N lrYZXX'U5׵1' PVJSD~a+΅,Ǘ{(J ~|k(uhEJ&F(R>aSv%7TdQl!!0[lMU$c(Cz؈MIQ0GB=df1CA}$JBόo'dnQxF G+yͿc X2B;£`kS:)Őjz,'-< 5U|vT$#'khDjNWV*NjG }p>AJ=&d2O|K,A8je/ /"925㨬pJlkv\?rVO{k~$5T(=m=ܔfmCB=Aߨ(O6,B"@-Xn06~~ޤ&!d 2'/<ϢyZ) k'Sx{lju@R;Dnq֒iA{3քm<+HJKrT 9UL2v[K)f4aN/N jLzQ 6H)đ<\3$uϏ˜ril鴳PJx7x:dZc3fL= yMf$ IRE?;l@&)g*G毿:ϖpv˅nP$G}}5Qvӡl4dLgAV$e1sGmۍ;qpQΣюD9 {~PtޑKUTvxBB{sPsڋB娎J+i>Ma}f|&Psa⑜p:qz VWPr޻S5q3 bI-T)O8QR#Dk#g%TIH%R'>bP{Y[ s>Vf;zA)Q> 6l5>3NlD '3tZ}b@1 H %,CSKEFSSS~3Dv{ZKI."dG3q/!z`9}mldp xrpH^m~_ȭs0'lh6^qEUqMEpIX]WxrF!Tˬ>ɹp<&Ȥ'{ {$h!FI⭘+a;Zi#ݘc 1aO7*}O4%NyHE$r4sjss-HDlU܍u;#rT2U*X_EPNIن,g4AEP+'I&i.ȍ$" -ҶOQ`< `0G0J$-KjTah'o' ' "|0op% )5jX+qqfJ+lCw Ǝ+qlh$Nj\䑘o=`ڌ縟/%8ķ?7T4&ʽ Q& K.FuØh|{%Ş-J-w'_T璑BjmDS)>qah&>"|܈aYT0'}ĝϛUؽ kdU!5;U8m^?ɾHENߛ|`y~co$Jf"3H#GqKe@.4j^̛O|N/Ty  A23klzZb7-\!> YO(GA0ޓR=??&s#Q3:'mtc+&sUqEzTM%}, 7[C%/_W&kFM"0g ΜaQ-QyMz&c%6+}mjOu85ף ߓԝ\*h{'qq]c {NZxu EiDZ\+6+ѭ Wޱ%F0ʹsx!z^z Dz[<H/%3;N\=B4EL]c{,PS lYWVTd 1EEc9FÃ? .{!a<7QS_]r+k9_nIu[B_4LٍCEfbH 8aMK$l:KWŅRU 1Ru3}36驝ƚ]KlZu@`s$\oRqh H-ID.f&2mz0r HkRWw1fAq:Ɯx'l=aj^7̅jHZX2)Fzw@o zm~t[xdKBL:+ b}F'G'1D6 J(5q0QVpeS'Z7Ok Ɋ]6t,V<e:ꉪBщjatB".8?\s : !8*Xׇ Dry~;xxYѸQgV !+ s~X,Հ|`-!? (N h6X`Y'DA3͚' W!怅ńΓx~j.<8{ ^h'eT e_YX8Q@|1Zc\ṓV$lV}xzrcȫy];Ks,Iɘ/hN"j"%PFvvIɥ10Gu"\xCO"v/kzwfo*'ҦNT/$zFqFڭ)oY2E?':ם-@ h +m0<0Vyf=ǝaNqIe&qBL#0ڝ2hu0"L2Gg@݈HEH9R;A' )0| Il釁C\}d!voj6*$1`j$FYpMygO8m0%wj4Po>f{/+n~9 W$!^Kmscoo*̑~,Bub[G] ^.@'h&\xOϤZ֙-11rdJ 5wjq1u?Vm'ԈU7mA1}p> ѣ*"55ćG`c2@s}1'v6P!@S}Q&~xyVz)+\6Bg\cj%`Si21:EA`.[`LŎ^|>YF0+9g޾ōScL5p![aI؋wi?vztD + Jz4]y,c';8"͎@@ Jy);}eS(↏:[cv~8_kAN _Ԇ#K Fx2ƨ%$ 9y=;pN!jf@~QF{ DZ$.ɨG#.uxDfOZ9>h^x '9ɔ\x|Q]W xVΧ'4 m& ">ÅsnD|T},2N@"Z,y'"9'H1u& :FӅy7@2[SB2W./3ktz;CG&Rn ]vxb*வ230.h gЧ9oHO̶yo@2zNI,"8  lNJbc op"3Ӣ417Xt@9?h/21OHF_џ#2Gɋ T<|鲐4 \_lN`-1&%ӫc%Tc B:bwa1آW_gkwڍ9*s;}d1%~O q{ ,[ Š^:u.xf >#ڑ9G44{-'Kٍ5f9G#E!_-edeb\ 1o Ybؐ+8 :A/{sѵVOt*" =f/'#f;rǔnஶ1:!_bcH9ρLF9\.9r&2⩞D1=r0 }wRXPH+Ӟ46B澎W5ڲecNeXgrtl9&{́E]] O,D=sSq1\a4ɐ̅žkvRzySuV$pEHta&#\krݣ+&1GTBcDɤ#Ƃ/.SkhhU\=2 6;. J)ꛙ9dk$ۅx:%?V.(Q7İx$qdoόYM.UA; ɐB8$R~uF) C0޹ 6#D!1C<+OJ7ڐ*zGo% #4Ă_I;o \}̙C L9}3.iܞn;3,`8o&xdpkoF DKNј}I#D3c :~zN&YKB#`lr!UiCH!0jAšdEg؜ҋ3C\ꊿ c@o cWc/Ġ^*yhkى>gm f^5}BFdq ',hF@J:xoW<y#䣟O3}>-h{c}1N'0FyB[tEut=%1.)GXA0RI@φnX0-"AC_7gE,{‰+svӝm'jbnl%OO o ^O7a'ib,&cra:';%4lNT)̑8nߤ8qWet,޾=CNX6c5imR<~g}{=( k l:=Z'yQcpAsq.(ch\.ey1-!y*JUfnVQء)b2IWlsuJqξ!d%d_]wm{˫O<|G_կg_s~70:;8m퉵 :c`.?L~|?MT o5l\*zTJ #@1_y>m,j\\cUwGG8b¶9Bls<]"k42o'V:&5L;7[O3&(U"!9 7lAL=_%A)e[;Y2d#Ό=%x^beGݡMGs@( qbved2X:"{IݛsRA1:.zvwbtQ摍Zs u5w2]k78IHS% xBV.f0\dkf.&)fm'__P3>+ks/.GEe|?o}^9X #TLQoGހg"4cىآl1Gwea"-+RKa&Nfpr XvB=W?Qr O9wî Ym<~rFĦ1#t0饝`M5s Z].BRݦn˟*HZCfoHF|DWxBȋ"Ec ']?# abAV_$U ١ۋ@7{SO0l8 *$`b=AFXBړH֓ʨ P' oAћ~f`+5UyUrZ[d4圈$ Y'LfތF%EЋ; wv[=a@twAk yK[ީ˸_QNδ!YwDbXTHA  vӍWN.eKqBSɫRo&t>z?0Su3B#]iʤJ'*Bջ71큒3)+R"ly!mhntš!~X"cF R) ̾Xb9ʦ~,NGH7:v=?%>ǯ =Ͼ< xkӱhCG +Ǘ?«s4\ރ(F{yM=@ HЦPi\b&A 9䨯JNmB07Pֻ̻dzes/F,†pVtWq>D2beʒFAez0XA'ۓEFֽwrbU."ŸNgX+C<;cPőyu5,lONɻC;}g͌dat1q Wxi )bUXfZ%.9qH_k^}r{,m8Z %˓c9v, FQ\VrOwy\" MG7K|j`m m)GCTJxFH!N]By( !9r#vd$tE޼=Hۆ9` !(Q)\y6G*?Ty6VJ"c-Fm(JfD}`T;ID% y:"2C{⯾M~?1oe~>pW{Oл|?Gx"|Jɷ~<}GOO~_lW7yՕ3?ʖ&7ˢLt>ɹ'_y}y8m)=]v|cGJ5YHNdNNu4GU(Řx{Aʠ1s'qB`IbI~ u"$7O̯H"/χ[KRR0༝ 1PɴAࢄCH$>1\9;rIS#EM=@`Bmt[ l@nηӀ([VJ@q IDATyOYU&ndD0eeܿ9w3XOiՠ"S]֔@6^.#r mQ44] :0Zx>𕃨'T]Yœ9x.p6<@=;4"qDؐ1k"67ͯxU*G|ǻ0+>s}3Y}W;y6>|)cl|'\7/7)/>O}qVq޼~p\Hư>#/NM~XkіF`Fa\!aw+V'#fMlg&|ycmRlDR2 4AW,sԚ8 t0&"IBhJ4cxw&(_OcfS\03f|['{spAH |v$?Xбdrݜ2[V7Ш.nI#!/׍9`+=0yEtF%!59v~Xʮ`7VpayG\NNt#$sci1UTSa!YpusРzc-Ax^7Zw'!G퐳%jk%R)(+Uz?q?jA(_?3GSsuf<2F:n\Iɝ˧?g>q|ϑ@|oy̩uJdBL~FDT872)`M^[gw0ge.oQIfup*AX-={s)S"HFggd%Ì}˄izZ#*Q L,ƽFDy?56̨ɳ\対b`L 䔐(BL6i )r a.60q\lE@fLJH05Rv1yd")e_ 9(FлBܲR T2}%png'$c˛Ag7˟@/w/Wq RQ9<)sm=dҞ^;/Da0B$B#iNPQ326}4'lN!"!R[Nt I]PY/#OOE{qǖ'OB0ΛC=k zrq욃ןv6l9QU{kFĜxx؜>7Lc|dI=3WM)ezѰX}b u>gy*k4bPJrq$Al;۶-WǝmG#,rX+ћUJ! hp.ο}P쐎1&f Rχ!Ej-ғ9*$%e/l¼3L$?\! ")9~%Hd'G A:c6f`Ƈg_2-G~gՋ@X&I #dk訄 s lQ{/ywlk <!slɃ;W9Z#\JH"JoAU|D㎤p{%D'2iѳQoqTfe.e@/Ǝnӕq[a 5Ct\]NB=Gz )9lYT#Y.T%͹/X<^ XNڮuZoF,QJaJ)IXm~m,xx㕡nx_V+9rmگHe\JF[fu0h;+̔hvLbUu/ ^ƞ9ϔs40Z:x~vRۍ5`hOoέVzN-ʚUT G?Y`IkKqڌ @i&1(VYәz'>ޱ];ue AT8sM jkx_A@ ?gTx~OW{ŏxk}e >Qv iDN2]/߹iKֻxk_2^'eŬY#c)!ЩLXQJrpjo2zEڭv}@% Y-ӥrY' QvWcic0iQD(},5zl¦MS՛u3+[^^Om~QSTްR"DSOԄNN1u>戰;[\K'¿ E9";MֆM/%}̊l10e~ӰHW;l Ywn;|&xj@(։5!8C~t&᜝ZK&A$H i<z3Dg0FjY':Gau˒W4Bpzn}?_M8+0Gr먴=sZg4a.& ')%&-Wc CQJRepڌ&Q5Ci|鏾HX_>0ܡ}H]G!4;?`r{ M Fc+>O/7Zyx-,:o!ߋdLBcJ(Rl-}w e_^0}/s9l?sLp7@;ƚ'3,ߘ~ WrμS` XK_T {s.wjPLp#zibɵa-n~%)q~ubd8gdMRNēt9D)@H2Lm@V]}VFPCMb1l"ſ[9()148hyx˖(A;/%BDBPTeƚ9e'cys^<2*SkfIR+9Rs;0%h}(j 5Lk){Μk!nz Qʎab[_3~/7_W̷Cimf[ *8|L T]Q7=T#Dh`0WI/__\y#是m^L#sNlk.5sRޘC>@KRQDFsL̨g3fo/!pN.cNf3Boj1de9GeJ7#c`מX}F= L3)Y==B '1dJ,M f kϭN&\L[w{eoqZYk1f 06 rkh  KZ#ͨkBEȖ6 P)uwz3Xf詐_s HYh8r%dw왰bZc6ʖ9Oڡ 1@UNE)lƐ9p{.= D-lo|_~_3?(v]p54db-BI6hw zx9mh$1)|;z_[|?ww>+Nskuo^DVa}g'|X;).|U37$CXCPN)GckGY@ö-x'NULuԭ͇@+n%2\cROW9&gμ;:|9m-owk*>q['_=|ǼHʏ:g>/޴?[xOs0q(~IqL߮oFU߿$FtŒک !zoO\{~.nⲿw^:>O{nd@Zt8c,Z&q"XƉb΁SF f ?7ʟ}~:Ow}EΠhE߸ ph;. !M#zyn $58)j® ;x1/Org= C]W$BNFp[4`Ei&&`% 1 7J9p92[FRf̩|1 1"kW 5Tq6WoedaTkW5bF+MzinN:h|wлӎ0c%7Wʫߥ*K/J_,(-]9@B+)H+ I"dnh:>9w{Ƅl웦5JiN;10⓽GۜC{tQ 0 K1,&N/m4'2s&LLD΋l&P|!DNc?OD)dRD׈u4hc:@u9AGmQ 4i'Ȱ죉41^W+s1QU1 G)G\4osF3.xdnK3Bw׉V;-֐s z%BFsلjUC.eՌ< zޑx.F!<k]Hf$Dx1_&sv *^"7o`n4XaxEBH\ _ S0~o?GB%K_6@EoS,(>0 dYz=.'^=n|~;_ڕ> ѭ4N70NݭzȘOxj=R^2$@kY@;베#!0JPac(5Zq^HɼA87^k*ʠº8ٛ z,L Ex]}%2M&>ȻLfKDykJd_[0Ȯ{H]YcZ,`^!jf /r?7DUraTA 'N4]KѶ_&y!.L~n7|U\t[1Bivjit֡ARuPO 7=1|L`ʉ(ĵj^tu-qb4H0 FUBLE;|l`cKkesy~G>+?t}Jyno댏FNx@5 i&(1"WG}6a/G%L \:MW\wO@ {|I^y#q㊧W !3\Ӊ g[r@iWُh V66(Ί58BZm}'H'1Ա,GjazAԅr>v8="-Ε)Pj&~G~_|3  IDAT&)%,`[Ιoz!cʲ NA7ׅqKmw+=a>#.8H{7<,{{|o/yyyCrlq9Vj| )?/^1HSB#D}ޅ8Rl]{vW²ln)yl]+cޫ]]1F"w Q2{lg⽻?MQiӚ-&>mQ-f޹ꠔWD;n9Fsfmj+],μz+;\maBPwz-UrꐔA)6`?µImן x㈸xۏL?vYa y>NӾ=c?d(āhl#&5hbj ڬ?YpCRkpq8ܸ: Zۛ7R# 0pjdp/8D1i"i)fO)t8V # Svtg'\𤘙sA0GZer样JeIДYỺȨ> q ~LD#1|,~(ӫ )JtALF񖦙" uE@=z;JoLD&䓄<@:ԞE`tp]0Wo-N]ˉv,h \^Vlsh YV m4Њ:= 4So8-ut8Z[[pat $q6X8};{>q\mO<k϶o%vN%n[aj\m8'4Dw6.d=!gy- Wh|De AҎnq*ƒI kc_zSj5x1$"!涻\x{jB~qiFqơݱJTÐ.Fn*uKR -Qh ܹ,.w4zZm(sv :aH:ϛ{ ;4(u5ӐhDSUu]#ApmЛ#tga-' uBqf :]ӖB F8Jxv ;rL8{ЕM0*;!0rF{TpIɲ1{$DSWkY~y+eP tPBPd ]V7Q` 9%x驗w8WI)sgy;>[e6*7Jw&e~GkqjD=6l0tՉS%J %nC-!3Kr+y摷sg {ܑyR߸bX.X26h@uL.!bEB4@YVX'Pcfɞy3PDmebsF7bl᪁kp&Q/J[ !Fn&yfTF4xcxl_>惰UZ9$v0NNǓ{Ί>6J9v8+UkABب"9B1(녥w3Č!R4Q6/3i:euy}tw9pNuc(VdtSb*krZ6KP|hnӀj~Ǥ^?0 O]{>?>$+  <"מ?<&|O|q$qbҚQچRoX}dJC=H=s[^W6^z|vn<K;1X ~ *:4=EbTRjgTUQB !xH̠Ej]frjQ62uzOJC #fEZ8r>ڨueZmtnIw?U1X#$w^2|0er>Nx(+ zhqJ!9jvM"~dmDw̓ՋV7ž ;9WSt3șu5Y7w{o_9k" izT +Uy~?'{: | ,{/n)>wWpftXEeLS`x{8>,띉/79]\eu)hPBlLW:}X:5LΓ\\(kg7GBqBtPlOj"/9Xب):p!нCA}kY):iDzEU1~,wSBRPƳ7z XZ8ⱱmt h"C+9$u1*Jgu ܢUZp8WJH@Z 4*`];}]zFœ>P <\T3Gи{-$D' XgW _2ASnmԭʬɷ^آzú+b/:i y[gqt6SOG}}Džwhޔ@(8i ]}UH vg8& ȸV |'P[a?evy7b4v }a5阔=mP\f_;_>U/iGOoƣop*Nj/3oͧ|;n_ߓn9xQM8:v`O8fDx+hHVib,zğ=7|#yKq ^xznpߍH,+'Y[o4J 32* \;#̬KCL ø7qHt#'FDV./WP7A&ZԱC~"HGk4nMbYRdaӑS"M6=P6k vluocڕC1T9o)Lư(`Mk'vi&ǃ+XО€ެ*\mCjτtDk}C9$|{Cea!;Wc9e+[T}Ô ͂6C&ڕ+F0NG\R\i]|l&ҋκZck֐ )XO\t :v}~?KGKoڽWԷVn_|Kƕ+7=_s>Y8JWyǛ{M͹)i0Dp!"q:9'W"y W^*|GHx緔^v9/ܳLg1#3Օgd z]8-DZ-pHxӽ3AD"/9nZ_C7V=5D5cf `ZΞCXNY:6i/NyOv)8u ixo!}N8zcڻUť4a9J/}nzVL^p"ɽhqtl^`979؇ )ģ![U<9֭S٥I; ~sm 72Bt2Y!pqp$!8yG^pkJݰQ8M$ ME"a Gin1ێCHSm̢rdY 1WkQ7OL)^\ROP!+XO *='kv4R(oHaV1*jNIqV: @VVe >ᆙ4DgQHi&&XG%"k=^j$P$!N;-, MbT;e5_Jj)_1y5nVш!y.Uři&jxu73cPŐM-59|gi}PeEcJbDuzqDXWX{̖,CI~(BL5ʯKW-AJȁicⴰ,R;AL3y(֐,K&a&C3ǗY>5wAH3xWE}sq:˯@JGp =»vU4ϯ={V^ZNvlnW ^&K/3?7wUT[L9?9NsϕFHp~yc`NY6V^n3I.;ncEV5r:]p5o塷>+yo& IDATt5\_ޡ=wca>:ޛ'B0f)mCGB<?rJ.X9)~'x K Dkcb?En@/wLӎs (RdɮJ?]C&c8Ɣgaի; 9 F.m o?v+eŪj1@eHX$w[hhU]:?2, b!w[K5jnJT@P>(`6:85J7E)3(JMzIr#q&- Ʋ\8q"VEDɳ,!G`;>w/ҟ~O~SKoP[GqU>xG}80:{3w糤y{:8$Ĥ@*,JVVڴ[??ekwZk,!A40s Ow@JڮAss?wgJjJl]#j\A݊uC+c(0m5sTy}z\qaDzU 噢%llZ0($ loll>O>BS[H(kzB)1}zs?̅5z8pd\|#6yR;i~:xQQZ#d"$!L*eŪ\A.r KbE #%r^F(Ce=0 Zy8Vn(ӨڀXiՕ$ЋHгKHOj1PW TXtIbj'"[quڠN3 1TY[| 3C0>E I;|(Մl %& v-S<LF0 q@kV`", YΊ8{lUaOd0g+3` {! 1`S$JKtm'}~u| 7E4bEP5¤s X?{|f{<%,j'255vr#Qk (z/ +Ԛ?xR eJa9j:c:>Fe8Vf58GFUN"9?<,fi\Ƨĸ8 +IgGMeԛ1VU+uǶ_`:{c*>v;wo|6!E :CJ=)Yrtʐ ȶ"0I@?S+VD+'Drd$DJj$BSJ&"MF8HLc@MZ nЫUu_Od?88kHR"(8IAcP3BGc ^Ntf́CvL(Jj@cQj4 DT8c0YDF)oE=m™Rk4ڠ ;rZaQZSM".qC5Q: SJ"+*Psa"t^)$Бs QW ^y_['TSÐ&vCPsfӌC!² "sr Rw~!~s[~I_ %L;c)ݙ{ Fa#Zhnd6r;\sen)JV8bLUY~}Ӻ ?/z&ebEzAsEf 倭&kP 0btazB7O\|ޝΖasg\V.`sb+įsj /^Ôȣl ХNY抳E7gϿ˛n.oƭCч3D6%}O"đ;PvZE.$ݿ֚Ky:ASkTV 6#)4ec;KhL-$lAn{[TV[H@){) >e "ikC. r["`ZQ"uEȚjEȑR#+!i@,G;.5IJ>?prM~zGY)V` Y#",DfbRV24)FҐ0ufG˜d,t39 ]2 ceѧxBL)!g P"WX8=Ykoo}_M4j8813m\cH-d?[Ĵ l{PWbumqkgpP֍IHֻk#%5=^m{}nょ;=W\{[CU(ad83?5RTΉ 1UY aȤ鳌1t} @ʚy0>,HGI} H*:`,`4x2ڎ %NX- W}o^=y >F.PB]fx.W0urej>?VxzNel ߍrn-E뵑hAS8~XCɟngׄ11DN+{#[n$8*$?p!db9= maŠ8dl2B–ϼzqVܺHSUst̝uz{G94T[,Kfǖ~3s; &gkn'OVwm^d215F9K91 m";bnkБXePIʂr XE#LӊR@ m[ 89#-DꙩSGL8US݃IcFwuIPA "-]BN~^& *qN.x㭷.LGǙc(T.ܽqJ##pnK:j'mH}PTO?b.8K??z(/0dlkK,nq{*ZL=hE TSʈ͊3cedKe5y(ݱI1̧WikMc-=F`5&[*MT Xi$02jEHJJe|S䒩j-n EbH2⚌!D ߐrD{'svg_!5Dm0FR3Xc(M,LiE;&GSM3,Zie0o6q)Sl4-i2k1Q U Jn+a0 fIRN%+Zqs֘Qla3#^ag6w<$f잃Ot/N<%TN&!²)4 \;-|˟{:an:81&L~j7U(mqUKЪ{~Fb?;a ƷBjũrfsbJXjON8SUOg>pX+͵{'+Nf̶,7ONAO J|Q(w F)&¬FP_@D`UXGd4:c7w!3՛}dsvB" X@g5U孅" + k3,YHyX|2X'%XZY2LrO=TF|?SYCk[PN0Zd5m {*bכmj (;IEJTFYE2 o 6~ˈ$BL:Cjkudj=FyRՆVjhZ\YsT$$jĶw [ _&FQILBfi=S loJAj,L| ǿ'y2)Ws3ptnѾu )s6xY8 -Bcָ5"j( ?RA5g8~r_>u~dE/(|QIUm wt,r lL4Ͽqޥ}S}2&^x.lZ:gs\~&(%ikesl fh]oLmQolyevTՄvnMj! Q^ ցj xBԅg!b!i }jYo*(t UG& ",VNB"@t2yc~?S}#fE`u1IUqJp[yDG=)eVKaXC ,QG1Xe y$LeVJA\^"h7AWy6Sl k R2@)~{̉ņ, b؀f X*GFCa58Ţ~Hceqٿ/<}~"c\8B7Qz,v`!%ΫIO?M܏L'?/̚S1hRQ Aa'zf [DN(6vHŲy g._Yp]C lW嘰gMVЃ@*/zwZ2fs)vrS= L\4F)% |cm[\Yvcb+!Sˀ-kQf֫F(,$]"xQJ$sqh?e{kJ@$; UA᪎xGc5KQAQҔցWyB07nĒ b&kpl2>ϋ 4uMR ?<5ªzE!8v )fJфaX;ZB4mpޠ:臞!ƔJ*xmpk@R> Z.Vb}O?vC dZRqʣ\%Rc <eaDV={ `ڐF&9c,h05.!wxxXh|.s8# Nknw擟,m^WX2C\GTPe_CP,控ݘ;9z:xc 2 ׸vslO'(4$+ʡ邱-)J Zl b?#K;\YʁFmY!#cL;Zz䫼H?Le@UMT`l)~+6&20b3I5UXgNc?TD"eSfq%ibb+"#A0.VDЄL][e"q(X<8,~Vx-^@iI8vw{,/=hvj$'h9E˯$[8lmtVJj-<E7~' U9T,׺)o^_=GY5}P 7<o.gpď^=7`O21#pXxza[Je:F5?ܥ'>H30F9ݔCwsv$PpޒǶz1^/󝁓uQP9ޒdiL&w 7yQ[fډcٟcd( 8uʕS\ɼiP^CGtE3&m0H(%PikL8]2k? ő}!)c"ƂӅfbp'9U0OM*sZb)XMǑ4TfkW" UUa33} _TǙ@D[(0⍥v5S[潝vA IDATV4mҖړtb5,Y>1%˜c [WU䁡h ŠxqJTt02ĞѣA;;X,2mo7^)ө6(米 o"GS țsXd7trF8t*j˓o'85JX,[ )RtĘ%c\&@?eE*`Sdժ#,,`l+l0EAIh Ԯ&1aD *+Ԩ>ͅQabx婬BcPّe*A1պjPa\ }UT0F՘Rd2`Zf6ڀ)(xOxos\zG;sڹ%R9otŐ[L(OTJ}kLX V# $\1s}_(>= Ly>eỼ=HÁ{g{1v[ÙK7~鈦X ,1͹="S2=eKLFԊ-{+E0 N1~:[y#&[0r,6#sDɺg\8/%jgQsc<#eի'X'!¤|.KlPpRkcC S8ܖ"eaepSTP4 & ;g{cBS{Uq!$R8ǡɨbĆiՒ#VyloRzDGɑU7`톜X-!41{\5#cZ)(XH[j(6Ѝ'Pl39UQF})9\R|q,HȐV|3G)RZI!U*;PhJ $8PR!2*OAD1,q1S֣bTl6fFT\+6&/}ze.݊/F89`CU-z3`KRv6!89}ކ;>~)r9#:u`/C;дp_>')Gnp;gK7lxܸqԐ#*R*^z =N4wG߃Ga.srXжϞٗx|8w~d Tm?0{aάШc,~R1 ߰kuX;-<߽/gaZ˯MC|mf3xJNpt ˳,{dfJIVpl̤kۜ qZ*Ζ!O j2eg 7{{g~KlN'!ڎumtMq#4B1N5J2ymTU٢= @#G2TacLMLEe/>a,yL 9 ӚIUUE0Ȅ o֜#6[TdtB}igL!X̀S%7!Hz`Hzlʌl& AHZQ؍Tق e@-k%83p2#860ii+Mk/avJvRڟߒ[  k ~Ҙ*zD`K0 =P| l+ep.+6QPؔs~ssM?՞7_*A?Y(;[՚gKbSs "r-s㈺}pL\uH]7wĹfcp|* 4\_K?қ˼/8L#LH.)U$Y7rxǃQRR^ܑ? kl`݌ J/szw޼γ?둦xket6|[sc}3/ 6E3)L:)UܖJ70a:I,.hYFW'$^;̛']?deyh{϶]kS8#9(W+ɢy&38=Q`U)p/rt ))&mom1[N PM lez٘m>1gO}5g3cv@>!$a;=\6zS<_SlgFɁ3>:Êж0i=Z p1F\~ *h6eY8@NQtFrJi޺{3w^$gt!=NSFd**šV7aMmktmɌ2ԊbhEZ&DIuaf(E)*+p+н TF XWe*'xۯП. 9 P[G-aF5?O?#]|%1~&:I'zkKP4ݮpi;Ͻsp^3 q!Q]!Wtg U#±bI̧ 9H?bZCd&)޻,^ X"GzǶ,:xVn$+x CTnߗ-ن29%(J2D)Y2$h-Wrs$Qw֚#{6X,t捜Vx06|5TbK*0m'qzѦBae>c*gY^~-vb=)rX 9꺦ж5u5ŖUUUAJkň 2s6D Wzk[wwO"⽥ wN*n\~:aX,IsL4V%E3)e0h]J؞QvF(Q@I4E(Ň .MQm1 AO_#n)ځ)%T^ RVė~w>}P_| * j{?lZ< jsˋVdn88|9O\ݼ+7?̼y|*'ʅ4!q82Ξ|{Z˩@.ӊ>joB2"oP+0#i|?!ʎ FGX ܾ,B7J-1aW^ܺ.' z+.-%/vP^j3ڕTӆit|' &49/k^sVɟ^ h5Lc$7e?e,/#¶O>(~?/!á+%ԛ'x)5c@gitb_m_}V¶˴tX+>sA}f<>YZxzF3ijT;S\j,ސJRm:"ՃgΧ OoRiFqkiJ6hy^S$3O:'l?=?2K~`] UE 8ڱ,ߝ&l:.ſg9%'oq|ukwjR\EE 8fqyBƁٿv;0=g@`)Ku~f م{g|ڟyߑb߾b|Oy>~ `6e%-O2Bn~޼BTe%4/wHrW~\–$5mϳ)k2|_09HM@ߗS*x/ex?^nx])4?78l2rs[bDh[7ey|s,AJ)Z"( j_2UO'ÊdO"cRV6 3gBhmz\,gՠmMqq&“5!=iYjMP9L'_|b$l6sP6E# Ir)4DQ5.R#$ۡ3!";w P>CG鿚ۮt8Wڦr2 :i:9r3(];ٿ ʍ~߽B*QA$[dG<*CԚd*z;q?Jvb ;OD i<4byt`36/lkH7;qd\{A 6-=e{7+T4 B7WO/qE5a7jȗA?wkBO'p#42%dO^Pn@ +ʻ /v7oʴbӔgҜ{#RHE/YfLhy8L!rwiT7p}PC6\<7m 7xMɂB,`S LߔϘ3}>|(ϣיϾ̼8Shت>D-W+lW] n~V '|W=]][4&n^D'vv_ݔTHEALT!cVӏv;)z iG =q̼f-Ci$BlQ% 7/Yp[h38n GmC yh-Ros;gԦokoG&cl! 䂱 Hض쐢U#U"X&W\VF1Fy>SDh(wlš"‰7dEb?g>< UDGP}aoڢخ!O`ZTՐR`9>Tuy0@`( i|awȫ?h>~~Jnzxi߿MŴ!}7}Q$TTx֖&m $aۖg>ρP~1D*<ˌ^1y?q<3M{mE.ûp.-~_}U  m4EȢJ0w:3L%#H!B_%!'RI~BŴ-"!3wHiȴoKKߵ(ݠUBI<.Dr^Ѻf1h8.3۞6{K;|QM-x8yTdJ3ܾl6]𫇲\73?Ϗþd ?_| Ɗߧ}R&w#Me8ٳ#DB('aT;Cׄ|?z^P'L~b%qqQMj&<05WtiDn>Fo4Zg9Ŋ2Đyx:"%M;Ȳ,LUYNqdE"v|>bu*\c@DIg ,)RՂ 99Ȃ͆_Cǽ߽Amb@&,_k> JbZzC|;ϊӏ55!Npz(]2&r8\S_|42RfܪPfU3qߓLzA{I?P`}F.4+,xZ5u KIF 8*0yiY:oXok~Ӗ'J=1TG)@ÇOVlY@pyU? rS9)FJ-^r~3()h|`U-> ;FI_yRSU״ dƊnKh[LnRh~ DR4uWpₐuUH5RLa-|mU+pn nϦ, N\"lV3U)" m[tw3uB0ˀ5TFD"l ǧ)0y-coɩ'$3ke .lWWW,s|FMvT/|#]P5 f|yyEq-HH)#ښq/P7uY0?xh$Equ"3IyNTJi1EJŇ{ٖ[!Ʃlɖ֐Z3C$OX ! *bd]Em߶Tmͫ,kӱ,Z &WwR ϰ +kfY_3#/o#j i^_晇'hjIZ-m`7  HZ!^mFWDL~!W̫^8EbʄqA!Ʒ,WRL?JGf'CEDXMxARXPL`]YXg]| ^^ @ȰК5[!〉Bhy;0itDj\H)P5!bzGd "!تfeՒijyu n=aR1!.X%3wmnćHe4ӑ.2Z&+uYYɳa{낢2 \1au[3'-`eN,.cMV@iEV5׻]ey:R[*Y-yDVv[AD+AfD)ŕ0AC' (Su3CkTb~'6ss]QK,A ja4z-PD#,DQh+9mQm u5vGɀUE<_Cl!DҼs`%{yaŎ`#!Z캉5BXrFd4!&XɾbU s\㚗/?mnipyz[DЊ)7cCjCj UR,^%N'ԭTńĭ%T=#m+,)Yܢ+m"):B8vR '%@+lJ%PLYl;h:Do TK eYЕ yĭn/މ+1c&w(eu[':l52PKm,6Sb)L(%pAec6e:A kxX[KhZ4Wc%~3Bxr,.s3pޭuSIr{}pbD J\yams)ј6J3MҙMo2LHL#o?ݽ;rQn7q:c<3NDB1{IVCyiAp38*^z' TeY2鉪J!445&g֗Q%Ǟ+u ]סf]΄1OJIs!VufbP[z^Kir,sT#T;w'DN Y2Q0e0ldFtIR6,VD#9P@3ORMUO>)l&~fY_r~aNts@H Ee_4i;^cC0%ƅ,ֲ{RzAɕAFs 'px2;2 [RMk G楢k'hX\B)aflfrQHX>~i<=[R,W[Zp<ҀVi̸PRP ]WjI3Ok M[NgP~6=b=ߠglSR0xOx1(PMG7#tl{no*f/a% u`W~ k<9 FP9+r.Z hچ'e@-^*[1' H `CmW\zm8 e--$o,~ (y>sKAN-F74a(YĘBC"T )Dw$fcGBL()!BJ*\p4mO]5D %H7t"D9ni)WL!HR4&'ش#%g~acrxk]#Th*ۣD6 rKm:bL-khPR7LJGhUt5$9Vf49ԫ$ai!VRy,,,!Dr.Q߽y÷X9'Ϩ*yg;JmU8%R:N [UheZ'Ze\Nh͗ b < gΣ!$ƳxqM 7爏Ptp@2VsbFi b+Y"8>/8~Q#DTT*VrȈ)ij'XܔO'm˗?Rtfss<>[|cHӴgl L]U$)0sR 2;Rrh]e3Uya(˺\3" ;K21ҒE"&O q1UpxF*GS)\(IFGHc6 +w4C`",X"yA݌ :.}߳̓[uJO gOeoejRJKd-<8ŌyB,m$u-HA#Q:!»'RգT 0=yFm˧jRNe-]@k+ryvzb\ftL0rmC[f74ƒ#B.%EBD,/kPp}}F֐ .1p^4 9 PjE+7-'crH%u,u!c%YGjZ'M ut>!4ZTBIOgLӄnj#}s@Eۇ{p?|zO{< PBn,( m[xAe O|ۇ{垮 @G$JTuqYwh R9Ӳm/vRO#,BT$f5ZӊUW ۮ'[#ZdTLH-hW:w 2/gP4239k9BȺ.%U͒eAfa] -#19 c,#:Q[2HY8쮸޿Vow}Ǧ4| U[q3|R(9 -VU1"Y`j6n8>/|,ΰKbI )!nL֋xJLedvޯŻQ7F#v42Y\`=9NeTP Vh"3XXqUTx949IQFJCeZYِӊe-"FԶ I[uhe +/XDZcWI^uE S%,s*{ H|#^stDLmˮE-J14%6nDHEUטJڞ0gPB"fFőE9Rk,kA) 8 PHZYJ~]MSm.D+Jm`"tBiEUmCeaV]{mG ԺEW21aۖ+0#P/诌rhUGcx!B.ilJ+FKR6Ñi|z%q~$H #gL畦kt̋\H ~GOl+R,a X ctŶ۠t"F]T0cAWYC&EIU7k >ˈnD0캞u"S|ou?~ɦSe5OSQ5|u"$UA(X'rU[QgW \`Y h2]u!#βUË *ub89YPR]{)r'~F*,~:nnyᄐEErwgٳk7ޱ.3;XuYyZޗ@&( y&Sg3;PX+ݙi h aZWNP8Ֆ2wxd" i3;MSՌ@HMw5 Dzs#19Oi3Jf&OHBx*Iޗ_0@̚%3`Knxy7Mu }Wlr B+CS{3"o4ڲ+P]:*JJDS5 gX3#:A5/~|`o1-=p7(#9E]o~ ~u&ȌR%V豵$"{R =@l7;z$R`-n_uU1`ky,AV-/znn }'I4X yE -BrhezͦW4UBb`jrTH*kwdYА5P6uVD̸uAJCzb.)K>!E%v &l!ƅz &)K 0#,# Dm1RET{XWwjzCe"9 bLWZ6F 0;Hukye]f4b8?#-BwD7dmt~t+1q4nog'JDy}f.S˿r⃣n2mWL&55RiVD mZż0kz⾫" 116kL.n0Zc$ZWE-ψQR#l] Z6|>y $ d~+>@ Ӂˆ H]٣+PeK  O:TZ9Z2O31&%bRJe)dd :(F}$đh!IiŇHJWb Dj˦/ J! 18N(A4 RIu^Rr;&RVd +bt}E]H@d~MĐQDjIWHepn94=uʦ^tE%~ / wtH8 d˖ *tuE Q==j5f4q[ hF3̰Kt$ȼꨐ.;)n9)vޯ Z,%ֲ؅-a0+IXRMk;q&8s;"{bl#H*rk3Fg ʲf$@ׇm@hŦy9dy7_a g= LrKJڲb\F Z ںzH4e FJ@1mHa%&a8b(rwn.ke‡;)i+=( A d"I|- Ep,OLa;S5iS,,W}OJ ZM Hv(H:,CHdص7e뀳!+R\#_~+\bۯpU=sՁqw詊Hhon J9MCߔtD(-=4yqfn۱A bp-e0(((R,0zOi,'%ƅ˰PߒyMShv] ][sl 򼡧,5J0%R%mǦi6.TJ#F cO8b!("D EJm4u;ҵ JSW%˼PU k"FELQSXD(ꖜHy(m(+e]pn(]"_*65eA̴ݖ]BGO ,kV Xk $iB(~s=J DJ͎S.?m S49jW\]C ^MY9k93N1Hm eP2`fHR%R4 (2נוqM4@#)y"$鴢#EAdaZ| D\Hhz~ĺ83_>9abcoU̯x|yq0HRĺjY jQղXϲ.,6UwՎy/9 }}ϻ-_cb/K42PD)!D&̘BP D,ZEHvIqW )o\wvbLScȜOʶ2{sKx^ 4!4g'XY !D5.:Ѹ"wږԜF7oaY]fV("0;RдQF U*ͼ8?G4 ~FDybؖmFA%^Xl)mz~,7@` Qes:!Ê%uǟfKT!6 xBhWZ㖙+m+PZB(XgЎiYy[|M՜F#n]#eES6Ժ,g3tRL]j銍 #3E ᪬ĐJ7t!JwD!TN-yAtxVXu?5h[qUl4Ӊ?~" %?/\,8gO$P=M> :b^3<2hۑw-O/_~yv}JL~{&r@ JyR}Mq%_>_9?`= Q[|Xt"쪰οrr:XIW|}K y^jGϸxOw) p ͮmJ4]ъ>~bݰisR )~afV_a\* Վhwd2OYx|b<XVm˶YØuZp6#uErxJˊR TupF ~|hsCD>_OKJ k%Rm,%VʪSt]xg KR̎:_.\'iyxX?S&-I>WVDm_+*SB]6|f8\RBhRȖ B\\H1 k{C .䊳R`]y:`*+-0DZ(-R` Aa %GnnhhAp9Ol6 e،B}- O4kA%rkS|] 1 \cB@p+ut1 eE+EaT5>, 8e^,?N{tPuSuC@.\.9dDq޳)%kzNZ1lH( r9OgRa""~#< MFPtS.)Ѭ0ЅAZU. +8 ""Ip̼H̓BCC[8{|=q}OO B)g[9d]bbN)c)9]"5:j%8>Xw;'+6<ye= Q|W|^\e`D 8۷;Ye[A Cnk(2g.kΗ _N3^%-"'n!ţ@lXցiRizv\eV:__~Ꭶ1vIm% h>?ϜFLH\Wf뚳g0Yѝ9 hSdy)0+ۮv 8Fx]Zpŕ:(+-FQR#A)%w;_'LQbsx7RbZN4uh lQJ7U RSeB5[\th(JuS{*xssXd3 (GR(.Ü<)ꊮk0IPpe6$\S6h=iv4!O~uR4 Q5=}}.D3 eH1鍩va^.8N 86Lv$FGSo(%ZpuqLm R"RIL)+!Fv3/l*I1?$ڠE+Uǟ^xxQHY%ݞ?|>ᗕˉ[6=4]w ݎOY֚y,֣f&\vw$eЬӅiF7LY0B)#u]\ǐSnEKW7ĸ0N3J0^f=Qaa%<{]at|_[| -(*e{JP.%QHDyhvP˄ |)\꾦n͜,900EEiL!p~ņ %T`ZWz g tʩE;oVi(ˀy`4%`\0+U_B m l8/6!q@2U.0o|>2exz!,tmEU (1c$R5xs3;lhi@&IUw9z u]fZ<).PDu#MRWQ$6xД ]h5~ -Ǘ S8%HT-6:b (h+eFjym*Ψ~|<mK15lozȶ45(Q1Zrf7;p(}.>\)rfCU /_Hpm5>D~g-(> 8QwonL uGQ7rZ__g%~剧iMS@HTw%Q&ѱZn+1$sFT=:fQ@Jw]U)L1YtR|@ȌjMY*6݁ю!P|b}~b5#KB!JD8 e~8 4e0N/fL-k\#R/)(zwk"duղ=xX ZHa& 1\ b T2w3)vU}H#˚7/I hǹlP:"EQ`uu@Iux"H,4M7F$!(JneK&eiޔu& Ja@Y)i=< yxnr |~aZ9)ٶ%_G2k$xǞ;vg6f~x/_KK5vL?^_9~aU(kj#]Wah#۫HU./DJ><ܼ! xyq|21yakYжȧ?q˕qyyiȧ t<_PjdefGQ1Dt uqdNE8xz9a' 5{|XQ2M!"R{QI#j7ʲ(u+.&eD ڄ]ۡ'и e"EPUSڬ-R8u]P2s$ 8d`vvev+HYj-FMArabtCJ$<ݴx-#~ P5\|awe)i; O_>sd!#wTB l-KO'\ΑÕrㇿDbsdm8l[u:<| 6)JI*4uiJ]j $nh 1w#xzOWp>s::V8j[E p| \N 7GzyAvwԆGaqJw5hP9(eQ`ZfB4mx$:b~KLYڶ%y1`H. . \8orP"R\iB[/SĹy"qVl7C!H(;O)O܈ )JSv Lnf#v5 SxoU>ŒB 5 /HEU;fkdv/nfV͎)w}G> ۀ#EZ(ɎLR KMD!ԄGrB(ڼ˴`@W4s2YmMn3Ny4u)eB45^Ye%AY(n5rIv7B c 2_um5Up^}["H4 f{Q7$nPS%Jhse,7>t0oPR4<|Srv!'IzE)J>|a*>>-:Á󗁰$Bpa ~2rX-_QR(7u]9th]Rxt<> W)-JKNgwwTEh$ޝ#S37=)zy`YDbu#h!Y<=q>#)qs>͔~jHe .[݅M]@7h8H ϔՆ.p:b]xU느BJ1J8~?}eE|%x)ϐz#@DU5P]'\()en:/)WʟQeBm7߽{w~u`]|qoZx#(@WNwL"1'rpNJOz!pUvK !Z&5g(+ .1_f\iJ=7[./Ǒ7t-FobϿ~Z: rtK\O+~el[;_ o1Z3y+?D}s)R hTD`iN/˜kemҠDCW5ӉjOUR$$EQC ֕債ŁH4 FKe"Q~RVtmŴ\2 dY։j1JqFBfЪ EIy9 i.Ͷhj=.&B (<+u9RZ[/~ʂtĹ+!5p4jm甕"I8a^0S.*MW`}(o@YےÆݮG}gPˤ1cuTsweAYvXdb\gV@Jw&ٙZx<D[inv7TUE0 C m"|uGv(mnkZ$޾*0m$2HQ9_cf2J ²fhNϗ2Qd)8Wjz,޷Tu%4e]mZ[ }wܽa}d[ GoV(e跊ݖw;nM=Æ_cq@`?>m_ED=OYgesfmۛ +RBnkb 8Q*xoѦ*;8cjA j|6ſycV0h%IH!L+!D)m3& 94e­oʆڊqR3ZWSćaxxjB/<_ش,9 ڀwþDoHQr>$Qg1rV[z IDAT4}$Rox9ϸxOxa`c ~*:N+! UHZï灟dv{#'g6{ w5=78:3ğ7e48L:)$x˜S?Lbps9x0SʃDJG k`[b4'~)Jq(*&Nגu 3u]Mp@*) i)5MQq]gڶE|~Ϳ/!լȦt7 ӕybX( 1FBXm5-KnnOLJ@ ,aE JR7=R*KRTm2y^-0lWjj68B5Ъo|D#]`G%VqsW櫒 |m$?8L߳BR5-)!&@ m{e UIU-M#QR`fR);g`#6e&KRv2])*>@ #PUhY#u[7;X+B"þE 6͂7< 9jDI[!H)+AӴR2 e mW2yKDeL7ߞ)n4 wl6-޻C.%!ԸU o2F.B*xÆKOЂl=MWjzTP1\ 0#^Qޑ~'Z8_?Q.#ȲXb@ӗ\0GDE ki 6Uif]FNc B, ^$%X!$MBTeOuʺGrnj*:;3:=Q&2 f](kf' 4j{R*y a;ǁ|f wU UGp@%UQ6L㌔%GcCf.@4 TJGU- [kQ" / /G$)@hU[X<&LØ󅗧+8Ĕ5!x h.sdRqZFQUgn,Ѕ.M[H1X\l=J <Z%V#v])LޭW* ԍ$H .) 5eLu]iK D [2U)IVKov nK22Okg ֍XKh+a2hBL&bZ~?e1$̳`m['!šDmcSǹ$_HR@XB`-ڔl7;HyWr{~{ r*J 3BHzs϶k1[.ʤ"R'G 3)U`ނk삐MOyۊ) X^mׅ5Z(PENYʴF y@pʠAĦ j(ͺ^bg,`%CBGH[yLXoAl1Si ÷|4\_fzdl>&I@8?%!(6乌glf7Շ-ornQ7in FѬ.?]<뜨H9qe|fF֜#.d=@J3NMl%"K/|zdgISI[7RHiGKJкӌ m:*-@mMa* OFB)(5"!.)yp}}CɊ"us!R܆6@7tm%1&*aUnwYlףeNy=f1{bhFŌBYxza5QKRU&J(% 5:ؾQ'#J::k6D%fEZ7Rr<I+eɺL_V7ɲ&Tt8783%Rf0 "b_~HS$&ҢUEH1`u\1nDʶ$:۠15k99wxo֙l(RDk%_ dCvѱw튏5F|Qt<|=Jjޯ3_3߱&6s8a;J;*JbB?QF!e麁 9_t|t J m.(Nt!5Ufr@=1EV>}J5F߾-8ٱCbMZ2둢M|BXZ|FICJ#%oHa'rx4]B2 ]1¾kF&r9? 9Jeg>4)} x$T DaŨ6P3Ƶ|;sSO)B,L=Ka'Fu a; ȹ_*R<9Aֵxh#2?QZ |z~Fj^@G.ϔ( 9)@eų{j踂x\ 9'œkO_k\HL :311+1*G~`6Yˌzb\ !r=x6_PڠI)EԈR2t bTNg -DiFɊTad vs{VG, {k Y&8u#? zoϿ'fK=_] !9|yyR',4wOo(CmOwvUcp8Jw3/t}d7R hкN?|xvlkp#)IR\x"_$7s>)JNq? kk)4v+Ȓ=UʡŘhp9q>|$WTXhcd=R Lc=A%ډZDʁq 0om2/Ƙ25BF@nxtq},`N ^ak̺$R1s *U҈,]ƞ<>*Q0Ֆ!%>Ґ%<y|3c Á9,/ǧ#mҮ4IAMr텷F'FҚ8Nq?}8=Z:]#Fn3Uhy{#o/T /[a[+B-|/~90i8\V9<~|aui$&XP_ ?"1, ~3GGGN;%#= >{V(ݴеR X;'^oT0V#&룊|A3ENX9KϜ=d]# 8m/$)z g^#eZ VDpNsCO R򎔊m푩̾EagrN%k# !qyS^eCʈ=4RMu9d="%s]_P(a e82b܄ɚue:ѦWJ\oP!"2߈C&z/7b\pÄ2lX*nl?cͫH)'ksEqTo*SUQ މ@' o5ͤ)ޓrBK;&: B* ZiWF1 Bg}a 7(5~)ʲ,8#@[hfYSD?h~yf$x6t!EFNɞ0W&ppt""?GбG0 !{,[n[ ,olނy|b{]YqI!{v>t9[X/_Ѫ(ah}d$xJ t:!aW(s]e;Ms@iVq:"& -[;h⤤IT Ri Jdݳ!ڨBB"'Q9$0=/'>J3) %+ N$>I;rG6m3R8%ox„Pk!}k-oϟM1]0pʲt`ڱzJ;\d7Є$=lӎK‰Ogo_=҅;Z;.+/%́ߧ |78$383JdX&֕\v(?#,*",GKIG%%B@Hd,!L:BL؏oD!RiМQ:XKe'KfY,R,@$Wƌȟ^w=NgT|ەf"~Zv=!G3!$*NPŹ^sH.m*, C@o$?LǑm7z>N@\4]0ڮ}c;IӧR R3 |"_~vhEm2;RwUW(P#aC`:Y<Xmu#F=] ۍ 5Og1kvݔ4Dx38m(996m]e=cmp;G[v#mXGJ9R1Z黑4#N[EJR;xJ+l[jaY@ !ZF+=`L&fXׅ~ptWh&+h̼mꉧ39b.hSPQa@-T J"| ڌќI>\EqX{3mDȥy*s >USA_#J@oT#cw.tFHCX0:"EF|$eZp@Hه㷕Z+!Ej-C(dpQtC>ǎ\B=FHΩÌ:~݅||T1ԾP1#)b4(&F]C~7u%Q$Ǧq仧($o39e(btHmB7躞N:r}o#;K$[ӣ [$,10*㙾~~C'RHyGQ$J5NmޛaW+-pbMOP3U_~F!hYRbEkH%%hcMy]Xm KCSvϾf; )ƩQVOP%qDRhW&)>S!׍M[ Tl7ʺv|*8Z&]f*7lX-!'~R\Ƙ6(>c }/QW49^߹^,jj(D1,Hi#JJT2clzB*7ڼ& svء%̂$։ښʪZG zjFJ^_|p9H %p:AM)m-R`_Y)#kne$t Bts)BH\ڠ$%ϺmĸrO|o@-3F\o0FB!ĵ dGaf5 IDAT+)H2!%N3ҏIu8ۣucT2&:xb;{؉16Y{m5 3Ӊiv_='S'UAKZdv{o3BM/Vu;8Xv8;|"䬈98_FqwOHkt&eC1rhmHR(#$BJ3pr:Q s_- 01؎~}%HgXȵ0MB b:dmviC<@ȖzGKʅq'/?=Sr !4CՓh{ݩs*)7`cZǠNL5T\tn$ذw>_Փk`"h";9nA[RJꡂ{iABɕ: 4Sƾ.h]1`"hSLo,1ܯ7n虗ŧVJh%R3Oxֺ DĸeEGHu=qsg'Yw(,J9PF cRX*;vcƣL$,U!uwnmFJpjM  ))bZ"iznBg'JW8g)!l3gQW-G+R]KEO ;RGE͞ک5M#ݯt6`ڑs&uV!R) sIPщ7a8LFPK!d≡n "1"u 䔘1XpQ VKa¹R4[;NTi'CvP:1uԊQ~i.Z,7qAidQ"emRW=!6NɕaH١%JMЙJ{ 1.)b * m@f*9qXeJnaUǶ-q1=>[$Be,R( %ghٖ+':eA6} Z4%ֽ2m[Y5RCm: 鷺ԈMbImq 1:݉,$5ヹ}SBƙVP>C#ZPֹLvS1 sXp_fl-QJqNA??4bdJ)I)5Ҍ19=?4Hf!Ѷk )`Y"0\kigh"_T2ZdJ9$+s*P Pt83y^ضt<&[\)̑7 Ud ^B6*vC+ RaFI#ҵq)EmN)8rE:d%RKVjpnxX|CDsO}"ePd8 "aځ,RU hZechm` "5vDiZgt#+rZ}"@?8No{DJ+1ݰZ!oP%n% P#_TKBa0tR 'NY fNk}{R rSbYnip}*ႵT )kCCB !$F18~Gg:z7!TSm^Gi<]?8-7BLHQ\Ir=ڎ,J 9ZnXʀq<ÍTZpeYV Q3صO?Rxt AH+Å:?fKCBҙ5DJe02;"hs> Z6Qʹ5"BHI +<~$2-Rj FHQ~%p g=vө`b߷V/b(Y{1)Z,9LJQ3>dSDrd^(5"TbjXTA6U[)<K939QRJ{!IsϐJ** X+7Q Jy:u(SHq%a)T!U %%(A- Ѱ^w~TCQ5 GB)vR (-y>_pɝe'ԆHsPgt>?Q)~{ y%J?~9!AVcB;}?%J"؍YusX#UHV]y̡F+v(B)a׆PO {k_)Hzۓ4 n U\WbNhiޤ%"a:2A#h7S@P8ZUlCbmzNәR+6Si ,jѝGjּ.7 3kܗoM1- Đ vVt|8#ǝbYGFk ]oZv2/ 1 [O|^GHH6wR^ M|Up_4\J jD~\v D*5lD?S_>+OOd" _kko}Y)Rg[6 j(0V#ZjZv|  r{ڎؐ$NJ G$ #_wgNJ ,D2ފ=ө{1tA[=rG%RnAbsΕ@g:b"0/ a_ȥBk?<=}}g_II4"\ɩʕLDJ4ݹh#2gÄJZ˴Rqmp%%0eL6n+9J>^Nhk yT(qqOJ0Uٷ BpL0)% $någ]^u=[ܯ%'h=$3BuÃdR~B`m#3iچggbn7rhUFH\7ޯ:Huc ۲$̾E9B!҂@_VIzBKO=(],|kǺ)K *emmۈqgtH)%QsB- &5` [Ƽ",i֍ix&}ZBz⟺g|~v hBB5Դ ɢLC/ڡFE J+vrkZ24Z13όR|/_\vVWCwϟۊr[)Ѐy||+?pqeP~r8 !,(P#U()m7}x0xJ[+? Bipnmrzk[wVq,wpQs_9_WDewBRڊ+!w8o^qq<i_y ^Vp9NW76aِ&=]BJ(URl{$56mcA,'zm+~_!s2환*ndB 81؎})GDIܾs醷ʠ"Sׯ|8 |ۮT?N V|,2Bwjm$@(yf2X,5)(JjFܘN IH i -}^mtˮLぱYCh'sH/R6DU%!de ۼcd8JuKGZL-") 97r>}ez~ZR B ́x<+H(%7rg@VʺϬ0{ gY^~ 9Qäc-/3uWu_I!/ZIrJ.h;Ccf)Ylm!lR ~hz.ǓhT': ?^ĥ)r\}1bנJ-@7eWgZ9v.ewG t8u`[**6aJ>ō}s"snvuG+fVXQbgpuHD-'{c]^Pjb_16˄ μ_1fHː3E&pzp=JZBʞӁ5n/lxƹv8 T#f%1hWJRƵHeNJ3FUr(#wW FDaw_>O(cعCiszk$9tJ);)ѝ 4(:J&HUYzX5=V$wwϭ%Yp31d6`<`3P*Uչ̈X-ylddf}^L#9kO/o?ot]>MҸT Gߏ?%ge}&쌜 1uB@],3WW[Vf-Ë[E?^ G靧!dW~0W;?##\o_ً lO@zEŒmo`(s LEc_Ypef!: ]Ѧ4?=iy;}n_f >~T__!}/_ ]2\߲ F?yQ3~3,\X;%?FWbxϿt[o;! 2- '{nӻ:bLy\9?WRHAhV>qW65LfKKll/l)`앏 Æ?tTyGAw}g4>!$h%6Oo(.Sx!_o{R@ ;%Y:c=oǪ1kVsL*O90~: i[, BKg 0$SQhGǀηWt\w~cF^w ö ~7@c/׳m.nǝ/O8a!Stϖb 5F_~vV(_Gz5#yX'An؃ǫw1uV 31vwzaY{a$k][tĝ&lDjٹgu{n||žv>s۳7^X:9@ rJD1Δ%}joTĎ|@7O' ³.-tx킵A 4RN҂eƘ)pJ߸~ WȘy~://_pVuL+sbӆV|;87ahI߿+_I+,D}y Z7Eη57Ue £ 9Ç8cX' Ems %~*\,^>'>o& zܸ˳,m|d8OkxQo\xb\.wn9reШ3:f4㭐Oz%\>n~o|x22x}-\?~~|I@|ʞ+[|8uU./ F 9CIkW,}tZ[k>@慆`={J[)%n>z3Y<~g_]?a||y~P'90 ʆRC䏟,xjB5xGJk1@ ^H 醬Mir`JkH&6"-am5k^&(׿||0%ܹߝ+q +}w8ftW_|rDc!v$'?$JIa#lXٶyȿR?◆Ƞ'{oc,~i+ 9s9M/lO RN`Me[<^ |xRJvTǮu .>PYg-cz zۅe s<K|f]QٙHd IDATρa$òD#O: Cu:z87zcpX~q*g*a}…3´Gc=O]}#c2Prz?;;)9-~+ʺm587] d8gcxϧqK<؁~Z/X&O8h3glv>}{yWc Wol1-TO_>9,ݿe¯'ny}se_Ѷ-yJSMgϸp]ᙯ{q,4K3\D:3>-G4#f@Pzſ ^`{癍ϟ!ף*OF,7~N@+'Q?Hs| $>]>D OVJ~G_GF|fQv}eyzo)яulJ\ Z.4 9!x<-9+VE)lJm 1FTH\ć;^I^J%6ѻ`1Hb`iBTT ޲'ֻD )`ζ]օ-CnU8fRCΩMn"cbbGma0 Rpx.0teY{O5Y DU}&+. º%pM,kcORJ1'Foj1xrm ,hȽ}v[e?mg Rx E9}‚r aǝ'?^3dKϔz'矬q 6^|ф*~C5f5k AxypivqD%P|7=WɇdAiOi̍g,/מּBɅ&ƽ׊(|P%c1r'{kg篭wknnF~vkѳ`'1tЍ:bT',j bK` q !,K7RXڅ-ye޸A03~,ƇFS~6W>PbQ@kyy:ꄞ+>E~6:3zY `qBa9a] ýU\12/3uO^lFr+G-za.8b&VvܿwnŎVVbVg g< 3(dBRR8Nt5p}^Z /\֕>mv; gw(ud}mHhE9ZK`yq<+XR`^;e { e_?7z j-Xb4q NQyz *o׈~r g;i83|\mB4|S^Nrö[4>ޡ c0(kYBLa5g$NX*݅bcJ2:g/ "Bgm AA4xg3h=нxgC2Ԛ'dF#>)9`G;+!Q2J gnA P;U$.R \`X  *B.E }JezưqdavmuCdz+fk+o:B-,C"ʓ@&Q!zG4mhVTTĀь+ZC ]3:A,&9!=h;_3c|!gdJ Jl.9qy^y++g*\Fv%glڸ`#4 u^` sc'Wjs'1%G`H)һabf!19kX=po&Ϻ ¨?lpؤ8flUm =.-lJ0?Z#ƣf$ώOWu|܂;Bg#y2 hmP:Z! !xVd0cq:|xL'J`, Fϛ:P(gh`'a112a-ei6[X\ڐk`F-6N:fy`1XzbFA14#x32wjn-z JB X0(* )K} =w K' +QdK ;s* OoObY"[qn(gre`OyFh}r/ׄY 1p [6Wh$o:d2`kZ0N(L'<ە1t01[&5Mܷc(lrss |O 07fjEvFJwϲ9hxoQg'%ò89K5Uts$Hbrq%L$vxcQ4[]`b1cVnI1O7/V-hw@{1jb0>@3Z\{.86TDJt߂b >:NMF/=&#!HS;t1Ph1$q8 DE,ԑyŅl~XOc2u\-zbt+:\1ӣzHV,D%Sr,^*;7 qbHq@QX&y>vj/[Y̆u`Xq¶B2mU(=\-G cY8C<a3&D3y[fT*m@pfz0ΰĕ z =W~镧5#|mcTXo_]6D gU{tOMATg9# x2LEd%zzhBҵ0\/Ϗ2P1>f`p>i s1v0tzZKr:Uy;8Pm ` xpΖ`8ɝ`" %"N"eka'‚cFц5 jȭsC}eK2q޹1V;M x C 0P2q.Q+ }uX猠d}p6:]fd{  8nD3{'HtsKx퍁#0Sψ(CrzyWlkF58%ώ˃[Q:Y~0NQ޲u~LlPTTݴ-|wnbM ,rLvƜpMf:*('m >ȍ}H<_,.ANJ^+Da`Dr\+9f4sHE?e#S. !Ew"Ęt!k0(GGћ!x t>ک7S.,%~@s('eZVTށ5g3#Nlt8E]Y:{ⰨV?۪"=VY#hr2|H =nAbS@mu@pirLJf8R@^Q*68Ų3r)ˊã\'5p-\fTDlȣ.ֈq+K7KvkSt ?Sd]+l)yO kjHᰜRf1>>[VP/ŇLH"z7VYKZ%r=RX5VB$ڹkZjrf^Nn.ɀk/Z+f 9 \\֊bS}*6lxoIJ>(1)bq$! R< .󵬕f 1F.SnY91H6_`LU` )ZQ.,$Ҕs>1THaX1;EXa3`tX9q{!-:}Gf!0q0jgO` zd8c') 7͡q ަ`{xp1K3 B uFJwyOa87ha? 2Rq8I$-a~ q~ҷ U;OZɈLuxQt jixQZ'ׄw \ -uNedrs[F-YbYfʫ`rBHkX B.<ɝu[eQ ~2g\7/+G).,kDQ|/IJFLQ]Vu$'_fNҬPj'#926ZDmՎ[4XZ.Ob(%Ӹ:y7tVyʘfe8v=ڱ-ߕsPC+q0O)(l1`PZF[ Bύۘk8w.o,K8稵0 ׆(5|@8NXzҊR?vu-kK0C ˅ucTVenJ\) jmjt҅9]XG^Q q[G/Dq(H {9kL8<:V8BWf2 0PRΊ8,v-T3ħmMoCPbD V h!ŕ:wr$oX{"Ƅ::*~r N@kC`A&EU;DD8ve,<2Uha[)m B~taN(,Jxcd,|pr~6aaGbws.<As'Sqb\8e0ȴx~,A;c҆j-ع׎XQo;L $C+݀wW2$ 5Tm`=RVHi$XT"A)bfmO.A[Bx'Z`ŠcoAH8102*i(.lJQXKv`4ƞNͅ&`\0{^TM!qGS 8#?h`l$&GL^N͊fe]goo,\m\'T,]t&/# )^>2 \t;}R0٢nI rd8P x9 Hё$qM7: fXĂkD1dՃN4r=-aYCA>q^`{J'1s'2mcŬ_Uw4WZa2 N]1&Ay,"6Yú,pvOu;rҙD-s] A=[>;jcg4y%GFthSJoVufk":?YyriêA}9ς"gdч2Jpފ2{V+4@PjxM`<{`I6֐#KzBFc k\&f1hYY0i>+Fγ)t:șLZmʩ0lT9º@ >x=]PsB`&X[Y|/<aNe +SOrE.HT%z)ĸፇZbI6 L<9)xn!`CԬA0JLR:F+1E.3T5ҡB@S4 F'"Z|  9AX)I&P3xrY`NtLt7 _bⱟ3^51Im=t;"x"F3l֨kq +Kd +I!1`L1hFұrtD8,̶^̭CžQC<|k^0#ItJq{ ?k) F m Lm0TNڞѮ2koGG]'3\Sc%SƲ3&ezǘq隣c&1ְ1bU0 thSr <4;|(:y:ޅ)q֩t`K*]b,J*xpl,TFS,8]d=kZidf: ~$^Wbt#mFIUkQce.3sjÖK ,'Uzo+LmRɠ `|ě@7cOYͤ`9scPY*tFЕYr]lQP"GaD]5npB4)hfJ>i` K4-:یGEJITAD;5Ũ{ϭ,.\+yGEqSr<V'18uSceF),gD8bF`pY00 Ԝi:q VRgEoPh Xs<]֨@8Z"f@he4&z ]mzqthYL#Y*>LyWwrp!$1A~[VK"ieycDAJr3iͨG孟XcKb+b-8v(Jǚ7W<R RLtx#Dh?7 Z*RDblD)!Q`ȠJ&Г+e4Vdɖ-l{Dڧ.tF  ZHF#mgWtaQVti'-"lqru>uY[)g>6}[6thx>>()ty9*zs1RF Eؔ{Uӆ%TXȬ?Y@{ X&%EUAKD 1;B~$}v5ٻopDyvh{ l 7I\h1vp=U1[dQfV6',^OLF$xA54ul.2:)lA$SWw rtkD k*[9Zhu6E Q2,^9-uT(-&*#jZ;r˙9q?@^~Hz8G95ve[ 2g_rD4yV-.̈́۶s?vF n:+NnrDz Na?6mnQ6*V~:agil0e2f-s䄩Зa Nu^f!uQBrGjkŻe] z 1suXI|q;v8_'cNRH<cESxu 7]ՉG˾VivdM\3*zOdMP*S}'uqIx҇sĜJ" y3_ne'@ٜpOC1yf]:.]Ncm EX K 7haQDH9s+&9GTSs^ŭmT [[l۹8!0+`ŵ:==i &O=j5=.mg;_ GVGwUO\ZsښQR  Kg]"oYV!cW8o\W읬Sss͗ Gyв)D##HT,֌_@Ư/z%ۗB& {0(.Y13n#mvmrfb(:(Su1 @חz!tҩq_}QR˹DƪsVw:ٶ͊FGL{ڐhUiނT;lU㴂OX۔{46zQ@zhBlY@sLO֚|1^Ѝm&.Urj( fslV5"HyW_ Va.a b"Zbl瀥ZMvMD[,f#$ern0i28]x/, K:HC[`]9;ݪW䩱kd #42k]r&ۂeQۋQ OXNR)90$%;@B ۞ ^ߝ#Aupm^x9`n\__ Q6/JEuqDSbg-L R7kF'qIZ 9A_ϻ HLl1 ۝磿6|~9iX[JiuH<$_:[f_|}a E$Bfc1PB:S!$l DEHen]@#s)zX1 jH%3m"X u2`+d^"at;Ǣ0'4(SA] : r&FY^~ вsڠ_۶0sg,/6khʓH|J659rёK-qʟ_/4%q@\6 Ĕ&0d9oB༞Ur({!XV\ 22)CL& .ni֢FD$AXt2Lݕ'0;GDRg Ę&z{Dlbc9m)%9%’@qct!oiG\"ݜĺrMG[zdM`'A q]b 5?~|޹wչK^]{r~np $LUcR1.z7F":r?v E 6䬕,bJ'#7/r Ɯe|*1@9bwMcOc&̔P4lشyy4Ču(Kaq \`иz^8oAތ9+< _$qVhZ+_TȪoz0ġvNr#) [D]$cd# UH"O!EDpd(!E33:И~S:IEc6rdW B›꬟Oskx]lsNBTȟ!Ůb4cS?O1E,2~,wAhM0Mªd1F 㝇1p E3* p.fwdzז3*oG{8JZu 5rBJux7ս"{? uzgbK^!bѷu5ϓ4{`XCE.L_|Bb58[̘OԼ6)/hO5Cb|2W#IZVH4X2AIDh}%D3ٝ/ƽdB<g&HQraKw#edO ,𨍰jFch6Hq-E"B f!/_`Klmu nǻSswVu|pb̨FQjQ"z2TeJJ3t! KkFo9 ]AǮ[j]颷eFx\'?XѠ=l38n;As!A K~Cq]*$0e]8rFP¢W1 zx*%*kN9МGipH9bI}UF$Eo }5Yz=fH0Ƴѯ3x\O7֚i堐ƭAUv9e,CGa-g˯MG|U7O5  h!cnu~?nS Jxc- gYnAQ7 ^`ŗw!cLuUO1%qqۖ{rP?"K8[$Ftq 88fD% '%cЯB`([~Gp ,Y̓K) ~]eE,$+:1MZNR̔xٝۧ9$DB,0`Lj7ZX}B-yɒEAxv&Ϳpk $iARDp9XDW@rJ?pk'爨p/GqZ>r(;^\a,ĠnMIkbSǎw IT5̱&H3үʬGCl!20I0w-;6ML7[^@(9cBk6Ob1h2 1uAH' ߟ+$r+m)M^BtNb Ome_&֖o[c5Ǎ=ч+8`OhĔ8NO goX`'w-DtA[[qe ,$GBP)ʞض-}rdNR_JJ{Aޠv"V+ZRYBdŖs EVcLWRDc.b}ؾ!e ^ Ƥ@82EvZ@H˘crJGvҫRj.Hs0^BFdEAq-0O֨;aXF dB1G>?y-K}1YmT󉲈D 'Zu;)2#AЋE2%$(GeIHS:=w<ƊP$2cD7ee50U63);24Q[ lJ̜ϧ71BebJQ~3[sVb^,bۦ,C$ N7M..9XOhVTb8%(:+ u0bh^u&cq}hEbF2c䙛unɾ1~#r*7M>8eV%X:8BQ)7TNJG%?y]Q;_c@N\Ҝ-/8IgrN.[*SeN_nIFy ߀wz6?9z ao%!qo cݜ:B&2ݙ:sRvLN Eh ƲAc}S'̈́֋i`.ÂSUOlvr)[Bmχ+!U.Fm,/zvRJ=k=Ymq]؏.V 9)AѭĶ}y 'lہ"^b(;XI U/$6/%keQxgxCdN {؈:&j[e0IJ ֘l~4,Kd8LM/Zz1X|Tvx$r> a##o]XbӨ ,r`4:_9؊W]"|AMQGw}_hie|PƒhXޜ)h\ Y_\}7Bj4.talS1]ى솳[34@; r%^"uML.7dEHf ,QJ ZWů14xX+}MZ\ lt{<ͳfGpY#Ɖ6亸JOcŒ" *I@8Dt=u0$1r䝯S&Ja lnوid,:6:#z#}q~MfF2)Fׅl:!=_\a9ӛI5N=n=VAOi}Sdtd eۼ\=m9"& Sȡ$T-f .JI2szwb"f{75!$-ĂcGOc#m6:K18%BE 6^`#X1bݰb2G L\ T`ڢ@z0e]1mr/ecy3‘gy\m;_o>if< 9KbȱI|֋W\'L#f{+AiC(kup=.EH:F*A9>o[Ddj=T_GVSPA?uLͶl6VuBRf05m4]FꦦG[b;J,iMkCO|^7AIllԉ _M#SEē'ۙ3bvM`y+n'(P7bN|~12ZR&/wLo&ĬOsPd@o{#hzOl U3Gbzź/oa#mj_> ڲRF U<ߴh;Vsd [J'!$> $x(h H t{bנ Yfm'Il?E3>*٨n"'kN@~+j-SOom5r&;w;P|s?/Q'EhnOM9b "ōt 싺X3seگ ArSYH|rlQpIB )( c/L!<'-d+I+jy s;l#oH <cwyQiNoW?v푼EG 99*m d%^ˍs7~~#5N`˻F,l z2BLjVLE` W3r*lN7Z!2YՆ7yr*ƞM-*kC5`+Q E}s$TR ݯyU59yCWzbbmp;n{&.8UT/6H̑!.O&}Sd:UK|l円hm\܈ꩼ9+m lTFkD>1gKhJqvA[I E덐!l.DZ ͉\rK93kO 6-O-'$`z> HEnرB8LVc0* 91 ^Q.r yCUh$Y2}^8|k%}+ʿqc IMQs{Mw3aS[y̆z#r>WMwEpu^>,1! K$%ޘm5 [ESd*182| h;Q#Rr(O#SE'0DݭR_̖!DD͝z:df7Z}bNd.1aҠ496ՍWF/Fh4FĄ',QMٜ[ gec'n UesRgGK"D̉cK|O(;V/yϜq˓14&A"AɳñoğxՋ[8(!e, _$ѩ=kIDATrp]y>)i'7T$!mRܵ6+%e?XIx'E?6I)3:[ (mdBMB4M{>`ߟBf*g䷏tԎEΞ*\W'ͳu&n&^x:'K]Ys-e|6rѩ㸓p7er(۳U{X _S2rǓ{9~/hfdifʑ'w^F5:1ׅr%ˑ^VPu/_i7Rj;@H){Bʄ%vRƖ !ͻbh|wLEȂxxM 2ޱ޹  T l1> eShzLF'װ~){QT\"9%tڀ l?<7][xbԁV6[o< X~܏˽Ǒ`FX$m}^B'[Jp1rʬ]lmZlnSD1,dF˩8$Kyo\omVn h+aO"{u" :jnӹkI#W+"{fA2)P.gw+3"rPQ[~Y4Av=cUH h>&x;Z1xJL|qS Rp.zKB` $.k&CpJׁ_I,{6RRLo3=1QhCisehB &<Ң{ (hɚ\xzټ+C__/d=lM ^M9ڈ"KAu]JY3(=E q+47`,5fAAʜΓP,DE+U~:9j9I΅\,ˣnG߃>NZ_ 0Ę1vW'>2`>!鿦N2I'}M863Gzb6}-/ƸpCqY$8B[GJQ#ћ$g#̵!hmrl9=Q r)+4UWXL$8$yOk5=!# 2>S]'OW%'ާz7~A)n2Ӟd9Hr\6&1qX *6#{#(Vʞ/hsLzANT,Is+$IGo_(l>t>Bzgؘ,n׋슿o}y'dpaJN0>$/b6𲕑k1މCj@]4߶[Mb UI) !ۢMii9PzYlz-E0p}޼^=K>|_mGyR`z XOǶPj`v ƄRNwo8gAWccυ,g=%Up!!$ x,mR!Ă.UkŸm?^C&)O^T$TmO;h,zVd7J&9NCN[CRN:K87Pj6 `*}Tb|vGS˰sbDg`q#ҕݜ[a( oh " Q?3V>Bg"{l\ DV(Y*uX8mA#  'wXUS9<)Ma}VTuXħ~B֛*ǷOh{,Zه^J벂$ˍif7+88z,j伱m+hݢ|A ƒ#r62 9r|PW5Ya'J>iSu>#2Ѭs3$6Q&x~>fӸfŸb@ ӯFzc'% ѓB/p=6Z6 e z[_KW5{oFo ǿX8IB9[>Æ\]]tvv+E[~^(ɂ$'>yz}>mo?4Pw _T@=_W#DƑNy.X5Qi_Yy9zI82}vDbd9cdWno^m DFG>OCDhs|OD {O+ 7悟0ZEfQ` QotŠj*kط\VeNp J|9e#{;7,|<CW5{S t/;a:+havbW%DcB&QE$ uӃ?H94QRC ah8t98RPJrDlNׅG̪7T>α^wuPd8MZy]^y|ԥ˾׋=uh8o B/l][vvbIB`&% )֕(>A* !؂ppy|%ZI ctUb/{E2au2$3.JN;)%nPMg,TYx|na3uDgej7U: WM؄=,?@עnlCA;|qc DI@3PB?XoGb lA[cN% `Y2^=<Վ,eA?oS'k`pw9ޣYG^%#bԚP[g5=aFdܠlLDOF ލo](T*F)T9XCI[$D}Vb0}d' B~$'Oj+M G3Ռ11Fͬ.9FؒD ؐ(m;s<%e#q w!u@Ao T<c3"v7|p|NyDR̥뉤(b-˜2_97T `+(aq/ǚjٓ EB%TOq gzg-#2(,?MuW+?ErA8E݊h{R ~-ʾ%Kve B\'DQe/nn9tL1ͫ [9%UZ\`A_Jz{ lDb[3hZa J Q?N9ύ|\D@R$`. Q:H{!)fM֏=Oq+mdzxȢHj0Pg .eؕo{8zB;۞,R!DhSC,:2MUሙ#!a$?逸RֻtB'WnSjT~VӻMӢBqtjhgeÂ;zq#r8Sy[oYG2Sܷw[˯[z-eez k b%ؗPAIx֋W *w 8"Fg1xN @@ ٳgl q}>zZ\#j~]l4Z&c5@ A\_1/riy7o/c'Ъ9Of0NG@7gA)֨SpV=LteIMIENDB`admin/js/jRespond.js000064400000003053147600046700010377 0ustar00/*! jRespond.js v 0.10 | Author: Jeremy Fields [jeremy.fields@viget.com], 2013 | License: MIT */ !function(a,b,c){"object"==typeof module&&module&&"object"==typeof module.exports?module.exports=c:(a[b]=c,"function"==typeof define&&define.amd&&define(b,[],function(){return c}))}(this,"jRespond",function(a,b,c){"use strict";return function(a){var b=[],d=[],e=a,f="",g="",i=0,j=100,k=500,l=k,m=function(){var a=0;return a="number"!=typeof window.innerWidth?0!==document.documentElement.clientWidth?document.documentElement.clientWidth:document.body.clientWidth:window.innerWidth},n=function(a){if(a.length===c)o(a);else for(var b=0;b=e[c].enter&&a<=e[c].exit){b=!0;break}b&&f!==e[c].label?(g=f,f=e[c].label,p()):b||""===f||(f="",p())},r=function(a){if("object"==typeof a){if(a.join().indexOf(f)>=0)return!0}else{if("*"===a)return!0;if("string"==typeof a&&f===a)return!0}},s=function(){var a=m();a!==i?(l=j,q(a)):l=k,i=a,setTimeout(s,l)};return s(),{addFunc:function(a){n(a)},getBreakpoint:function(){return f}}}}(this,this.document)); admin/js/cf7-customizer-admin.js000064400000141272147600046700012570 0ustar00(function( $ ) { 'use strict'; var editor; window.refreshTimer = false; $(document.body).on('click', '#cf7cstmzr_to_fw', function() { $(this).hide(); var currentUrl = $('#cf7cstmzr-current-url').val(); window.history.replaceState(null, null, currentUrl + '&fw=1'); $('#cf7cstmzr-main-container').addClass('fw'); $('#cf7cstmzr_exit_fw').show(); $('body').addClass('my-body-noscroll-class'); recalculateContainersSizes(); }); $(document.body).on('click', '#cf7cstmzr_exit_fw', function() { var currentUrl = $('#cf7cstmzr-current-url').val(); window.history.replaceState(null, null, currentUrl); $(this).hide(); $('#cf7cstmzr-main-container').removeClass('fw'); $('#cf7cstmzr_to_fw').show(); $('body').removeClass('my-body-noscroll-class'); recalculateContainersSizes(); }); $(document.body).on('click', '.cf7cstmzr-preview-control-icons .dashicons', function() { var control = $(this); var container = $('#form-preview-container_inner'); var styles = { width : '100%', height: '100%' }; if (control.hasClass('dashicons-tablet')) { styles = { width : 768, height: 1024, "max-height" : '100%' }; } else if (control.hasClass('dashicons-smartphone')) { styles = { width : 375, height: 667, "max-height" : '100%' }; } container.css(styles); }); $(document.body).on('click', '.cf7cstmzr-settings-item-header', function() { var header = $(this); var item = header.parents('.cf7cstmzr-settings-item'); var body = item.find('.cf7cstmzr-settings-item-body'); var items = $('.cf7cstmzr-settings-item'); if (item.hasClass('active')) { item.removeClass('active'); if (items.length > 0) { items.each(function () { $(this).removeClass('disabled'); }); } header.addClass('last-active'); } else { if (items.length > 0) { items.each(function () { $(this).removeClass('active').addClass('disabled'); $(this).find('.cf7cstmzr-settings-item-header').removeClass('last-active'); }); } item.removeClass('disabled').addClass('active'); } }); $(document.body).on('click', '.cf7cstmzr-settings-save', function() { var control = $('.cf7cstmzr-settings-save'); var formData = $("#cf7cstmzr-settings-form").serializeArray(); var titleInput = $('#cf7cstmzr-current-title'); var slugInput = $('#cf7cstmzr-current-slug'); var enableButton = $('#cf7cstmzr-enable-globally'); var enableForm = $('#cf7cstmzr-enable-for-form'); var data = { action: 'cf7cstmzr_save_form_customizer_settings', nonce: cf7cstmzr_ajax_object.save_form_customizer_settings_nonce, formData: formData }; if (titleInput.length) { data.styleSchemeTitle = titleInput.val(); } if (slugInput.length) { data.styleSchemeSlug = slugInput.val(); } $.ajax({ type: 'post', url: ajaxurl, data: data, success: function (response) { var decoded; try { decoded = $.parseJSON(response); } catch(err) { console.log(err); decoded = false; } if (decoded) { if (decoded.success) { control.attr('disabled', true).removeClass('button-success'); enableButton.attr('disabled', false).addClass('button-success'); enableForm.attr('disabled', false).addClass('button-success'); // alert(decoded.message); alert_sticky(decoded.message, 'cf7cstmzr-success'); refreshPreview(); if (decoded.url) { setTimeout(function() { window.location.replace(decoded.url); }, 2000); } } else { // var fragments = response.message.fragments; // updateFragments(fragments); alert_sticky(decoded.message, 'cf7cstmzr-error'); // alert(decoded.message); } } else { alert_sticky('Something went wrong', 'cf7cstmzr-error'); // alert('Something went wrong'); } } }); }); function alert_sticky(message, type) { var messageContainer = $('#cf7cstmzr-sticky-message'); messageContainer.text(message).removeClass('cf7cstmzr-success').removeClass('cf7cstmzr-success').addClass(type).addClass('active'); setTimeout(function () { messageContainer.removeClass('active'); }, 3000); } $(document.body).on('click', '.cf7cstmzr-settings-reset', function() { resetStyleSettings(); }); $(document.body).on('click', '.cf7cstmzr-settings-default', function() { var control = $(this); var defaultSettings = control.data('default-settings'); resetStyleSettings(); console.log(defaultSettings); // Form Default Styling $('#cf7cstmzr_form_padding').next('.dashicons').click(); $('#cf7cstmzr_form_padding-top').val(defaultSettings.form.padding.top); $('#cf7cstmzr_form_padding-right').val(defaultSettings.form.padding.right); $('#cf7cstmzr_form_padding-bottom').val(defaultSettings.form.padding.bottom); $('#cf7cstmzr_form_padding-left').val(defaultSettings.form.padding.left); $('#cf7cstmzr_form_margin').next('.dashicons').click(); $('#cf7cstmzr_form_margin-top').val(defaultSettings.form.margin.top); $('#cf7cstmzr_form_margin-right').val(defaultSettings.form.margin.right); $('#cf7cstmzr_form_margin-bottom').val(defaultSettings.form.margin.bottom); $('#cf7cstmzr_form_margin-left').val(defaultSettings.form.margin.left); $('#cf7cstmzr_form_border_color').iris('color', defaultSettings.form.border.color); $('#cf7cstmzr_form_border_radius').val(defaultSettings.form.border.radius); $('#cf7cstmzr_form_border_width').next('.dashicons').click(); $('#cf7cstmzr_form_border_width-top').val(defaultSettings.form.border.width.top); $('#cf7cstmzr_form_border_width-right').val(defaultSettings.form.border.width.right); $('#cf7cstmzr_form_border_width-bottom').val(defaultSettings.form.border.width.bottom); $('#cf7cstmzr_form_border_width-left').val(defaultSettings.form.border.width.left); // Input $('#cf7cstmzr_input_bg_color').iris('color', defaultSettings.input.bg.color); $('#cf7cstmzr_input_border_color').iris('color', defaultSettings.input.border.color); $('#cf7cstmzr_input_border_radius').val(defaultSettings.input.border.radius); $('#cf7cstmzr_input_border_width').next('.dashicons').click(); $('#cf7cstmzr_input_border_width-top').val(defaultSettings.input.border.width.top); $('#cf7cstmzr_input_border_width-right').val(defaultSettings.input.border.width.right); $('#cf7cstmzr_input_border_width-bottom').val(defaultSettings.input.border.width.bottom); $('#cf7cstmzr_input_border_width-left').val(defaultSettings.input.border.width.left); $('#cf7cstmzr_input_padding').next('.dashicons').click(); $('#cf7cstmzr_input_padding-top').val(defaultSettings.input.padding.top); $('#cf7cstmzr_input_padding-right').val(defaultSettings.input.padding.right); $('#cf7cstmzr_input_padding-bottom').val(defaultSettings.input.padding.bottom); $('#cf7cstmzr_input_padding-left').val(defaultSettings.input.padding.left); $('#cf7cstmzr_input_text_line-height').val(defaultSettings.input.text["line-height"]); if ('yes' === defaultSettings.input["full-width"]) { $('#cf7cstmzr_input_width_yes').prop('checked', true); } else { $('#cf7cstmzr_input_width_no').prop('checked', true); } // Button Default Styling $('#cf7cstmzr_button_bg_color').iris('color', defaultSettings.button.bg.color); $('#cf7cstmzr_button_bg_color-hover').iris('color', defaultSettings.button.bg["color-hover"]); $('#cf7cstmzr_button_border_color').iris('color', defaultSettings.button.border.color); $('#cf7cstmzr_button_border_color-hover').iris('color', defaultSettings.button.border["color-hover"]); $('#cf7cstmzr_button_border_radius').val(defaultSettings.button.border.radius); $('#cf7cstmzr_button_border_width').val(defaultSettings.button.border.width); $('#cf7cstmzr_button_padding').val(defaultSettings.button.padding); $('#cf7cstmzr_button_shadow_vertical-length').val(defaultSettings.button.shadow["vertical-length"]); $('#cf7cstmzr_button_shadow_blur-radius').val(defaultSettings.button.shadow["blur-radius"]); $('#cf7cstmzr_button_shadow_spread-radius').val(defaultSettings.button.shadow["spread-radius"]); $('#cf7cstmzr_button_shadow_opacity').val(defaultSettings.button.shadow["opacity"]); $('#cf7cstmzr_button_shadow_position').val(defaultSettings.button.shadow["position"]); $('#cf7cstmzr_button_shadow_color').iris('color', defaultSettings.button.shadow.color); $('#cf7cstmzr_button_text_color').iris('color', defaultSettings.button.text.color); $('#cf7cstmzr_button_text_color-hover').iris('color', defaultSettings.button.text["color-hover"]); $('#cf7cstmzr_button_text_line-height').val(defaultSettings.button.text["line-height"]); refreshPreview(); }); $(document.body).on('change', '#cf7cstmzr-preview-unstyle', function() { refreshPreview(); }); $(document.body).on('change', '#cf7cstmzr-preview-mode', function() { var selected = $(this).val(); if ('split-mode' === selected) { $('#split-mode-settings').css('display', 'inline-block'); } else { $('#split-mode-settings').css('display', 'none'); } refreshPreview(); }); $(document.body).on('change', '#cf7cstmzr-load-body-tag', function() { var control = $(this); var loadBody = false; if (control.is(':checked')) { loadBody = true; } var data = { action: 'cf7cstmzr_load_body_tag', nonce: cf7cstmzr_ajax_object.load_body_tag_nonce, loadBody: loadBody, } ajaxRequest(data); }); $(document.body).on('click', '#cf7cstmzr-enable-globally', function() { var control = $(this); var scheme = control.data('scheme'); $.ajax({ type: 'post', url: ajaxurl, data: { action: 'cf7cstmzr_enable_globally', nonce: cf7cstmzr_ajax_object.enable_globally_nonce, scheme: scheme }, success: function (response) { var decoded; try { decoded = $.parseJSON(response); } catch(err) { console.log(err); decoded = false; } if (decoded) { if (decoded.success) { // var fragments = response.message.fragments; // updateFragments(fragments); alert_sticky(decoded.message, 'cf7cstmzr-success'); // alert(decoded.message); setTimeout(function() { window.location.reload(); }, 2000); } else { alert_sticky(decoded.message, 'cf7cstmzr-error'); // alert(decoded.message); } } else { alert_sticky('Something went wrong', 'cf7cstmzr-error'); //alert('Something went wrong'); } } }); }); $(document.body).on('click', '#cf7cstmzr-disable-globally', function() { var control = $(this); $.ajax({ type: 'post', url: ajaxurl, data: { action: 'cf7cstmzr_disable_globally', nonce: cf7cstmzr_ajax_object.disable_globally_nonce, }, success: function (response) { var decoded; try { decoded = $.parseJSON(response); } catch(err) { console.log(err); decoded = false; } if (decoded) { if (decoded.success) { // var fragments = response.message.fragments; // updateFragments(fragments); alert_sticky(decoded.message, 'cf7cstmzr-success'); // alert(decoded.message); setTimeout(function() { window.location.reload(); }, 2000); } else { alert_sticky(decoded.message, 'cf7cstmzr-error'); //alert(decoded.message); } } else { alert_sticky('Something went wrong', 'cf7cstmzr-error'); // alert('Something went wrong'); } } }); }); $(document.body).on('click', '#cf7cstmzr-enable-for-form', function() { var control = $(this); var scheme = control.data('scheme'); var form = $('#cf7cstmzr_select_form').val(); var mode = $('#cf7cstmzr_select_form').data('mode'); $.ajax({ type: 'post', url: ajaxurl, data: { action: 'cf7cstmzr_enable_for_form', nonce: cf7cstmzr_ajax_object.enable_for_form_nonce, scheme: scheme, form: form, }, success: function (response) { var decoded; try { decoded = $.parseJSON(response); } catch(err) { console.log(err); decoded = false; } if (decoded) { if (decoded.success) { if ('free' === mode) { $('#cf7cstmzr_select_form option').each(function() { $(this).data('scheme', ''); }); } $('#cf7cstmzr_select_form option:selected').data('scheme', scheme); var single_form_description = $('#cf7cstmzr-select-single-form-description'); if (single_form_description.length) { // console.log($('#cf7cstmzr_select_form option:selected').text()); // console.log($('#cf7cstmzr_selected_style_scheme').text()); single_form_description.data('form', form); single_form_description.data('form-title', $.trim($('#cf7cstmzr_select_form option:selected').text())); single_form_description.data('scheme', scheme); single_form_description.data('scheme-title', $.trim($('#cf7cstmzr_selected_style_scheme').text())); } show_select_form_message_button(); alert_sticky(decoded.message, 'cf7cstmzr-success'); // alert(decoded.message); } else { alert_sticky(decoded.message, 'cf7cstmzr-error'); // alert(decoded.message); } } else { alert_sticky('Something went wrong', 'cf7cstmzr-error'); // alert('Something went wrong'); } } }); }); $(document.body).on('click', '#cf7cstmzr-disable-for-form', function() { var form = $('#cf7cstmzr_select_form').val(); $.ajax({ type: 'post', url: ajaxurl, data: { action: 'cf7cstmzr_disable_for_form', nonce: cf7cstmzr_ajax_object.disable_for_form_nonce, form: form, }, success: function (response) { var decoded; try { decoded = $.parseJSON(response); } catch(err) { console.log(err); decoded = false; } if (decoded) { if (decoded.success) { $('#cf7cstmzr_select_form option:selected').data('scheme', ''); var single_form_description = $('#cf7cstmzr-select-single-form-description'); if (single_form_description.length) { single_form_description.data('form', ''); single_form_description.data('form-title', ''); single_form_description.data('scheme', ''); single_form_description.data('scheme-title', ''); } show_select_form_message_button(); alert_sticky(decoded.message, 'cf7cstmzr-success'); // alert(decoded.message); } else { alert_sticky(decoded.message, 'cf7cstmzr-error'); // alert(decoded.message); } } else { alert_sticky('Something went wrong', 'cf7cstmzr-error'); // alert('Something went wrong'); } } }); }); $(document.body).on('click', '#cf7cstmzr-settings-save-as', function() { var control = $(this); var container = $('#cf7cstmzr-settings-create-new'); var saveBtn = $('#cf7cstmzr-settings-save'); var defaultBtn = $('#cf7cstmzr-settings-default'); var deleteBtn = $('#cf7cstmzr-settings-delete'); var resetBtn = $('#cf7cstmzr-settings-reset'); control.hide(); saveBtn.hide(); defaultBtn.hide(); deleteBtn.hide(); resetBtn.hide(); container.show(); recalculateContainersSizes(); }); $(document.body).on('click', '#cf7cstmzr-settings-create', function() { var control = $('.cf7cstmzr-settings-save'); var formData = $("#cf7cstmzr-settings-form").serializeArray(); var title = $('#cf7cstmzr_settings_title_new').val(); var copySettingsControl = $('#cf7cstmzr_settings_copy_new'); var copySettings = false; var isFw = $('#cf7cstmzr-main-container').hasClass('fw'); if (copySettingsControl.is(':checked')) { copySettings = true; } $.ajax({ type: 'post', url: ajaxurl, data: { title: title, action: 'cf7cstmzr_new_form_customizer_settings', nonce: cf7cstmzr_ajax_object.new_form_customizer_settings_nonce, formData: formData, copySettings: copySettings, isFw: isFw }, success: function (response) { var decoded; try { decoded = $.parseJSON(response); } catch(err) { console.log(err); decoded = false; } if (decoded) { if (decoded.success) { // var fragments = response.message.fragments; // updateFragments(fragments); alert_sticky(decoded.message, 'cf7cstmzr-success'); // alert(decoded.message); setTimeout(function() { window.location.replace(decoded.url); }, 2000); } else { alert_sticky(decoded.message, 'cf7cstmzr-error'); // alert(decoded.message); } } else { alert_sticky('Something went wrong', 'cf7cstmzr-error'); // alert('Something went wrong'); } } }); }); $(document.body).on('click', '#cf7cstmzr-settings-delete', function() { var scheme = $('#cf7cstmzr_select_style_scheme').val(); var isFw = $('#cf7cstmzr-main-container').hasClass('fw'); var data = { action: 'cf7cstmzr_delete_form_customizer_settings', nonce: cf7cstmzr_ajax_object.delete_form_customizer_settings_nonce, scheme: scheme, isFw: isFw }; ajaxRequest(data); }); $(document.body).on('click', '.cf7cstmzr-close-welcome', function() { var data = { action: 'cf7cstmzr_close_welcome' }; ajaxRequest(data); }); $(document.body).on('click', '#cf7cstmzr-settings-save-as-cancel', function() { var control = $(this); var saveAsBtn = $('#cf7cstmzr-settings-save-as'); var container = $('#cf7cstmzr-settings-create-new'); var title = container.find('#cf7cstmzr_settings_title_new'); title.val(''); container.hide(); saveAsBtn.show(); var saveBtn = $('#cf7cstmzr-settings-save'); var defaultBtn = $('#cf7cstmzr-settings-default'); var deleteBtn = $('#cf7cstmzr-settings-delete'); var resetBtn = $('#cf7cstmzr-settings-reset'); saveBtn.show(); defaultBtn.show(); deleteBtn.show(); resetBtn.show(); recalculateContainersSizes(); }); $(document.body).on('click', '.cf7cstmzr-refresh', function() { var formData = $("#cf7cstmzr-settings-form").serializeArray(); var previewButton = $('.cf7cstmzr-refresh'); previewButton.each(function() { $(this).attr('disabled', true); }); $.ajax({ type: 'post', url: ajaxurl, data: { action: 'cf7cstmzr_preview_form_customizer_settings', formData: formData }, success: function (response) { var decoded; try { decoded = $.parseJSON(response); } catch(err) { console.log(err); decoded = false; } if (decoded) { if (decoded.success) { var frame_top = $('#formPreviewFrame').contents().scrollTop(); var formId = $('#cf7cstmzr_select_form').val(); var url = $('#cf7cstmzr-url').val(); var iframe = $( ' Open KB in a new tab/window Idea: Have the KB in one half of the screen and the cf7 form in the other half.
admin/partials/tutorials/welcome-de.php000064400000001501147600046700014234 0ustar00
Hanbuch in einem neuen Tab/Fenster öffnen Idee: Das Handbuch in einer Hälfte des Bildschirms und das cf7-Formular in der anderen Hälfte anzeigen zu lassen. Hanbuch in einem neuen Tab/Fenster öffnen Idee: Das Handbuch in einer Hälfte des Bildschirms und das cf7-Formular in der anderen Hälfte anzeigen zu lassen.
admin/partials/tutorials/tutorials-en.php000064400000000752147600046700014650 0ustar00
Open KB in a new tab/window Idea: Have the KB in one half of the screen and the cf7 form in the other half.
admin/partials/tutorials/tutorials-de.php000064400000001035147600046700014631 0ustar00
Handbuch in einem neuen Tab/Fenster öffnen Idee: Das Handbuch in einer Hälfte des Bildschirms und das cf7-Formular in der anderen Hälfte anzeigen zu lassen.
admin/partials/cf7-customizer-admin-tutorial.php000064400000002624147600046700016004 0ustar00

admin/partials/cf7-customizer-admin-tabs.php000064400000001623147600046700015070 0ustar00 __('Form Customizing', 'cf7-styler'), 'settings' => __('Settings', 'cf7-styler'), 'license' => __('License', 'cf7-styler'), ); $active_tab = !empty($_GET['tab']) ? filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : 'form-customize'; return; ?> admin/partials/cf7-customizer-admin-tab-settings.php000064400000000645147600046700016546 0ustar00

$data) { $plugin_installed = Cf7_Required_Plugin::is_plugin_installed($data['slug']); ?>

-1, 'orderby' => 'title', 'order' => 'ASC', 'post_type' => 'wpcf7_contact_form', 'post_status' => 'publish', 'suppress_filters' => false, // подавление работы фильтров изменения SQL запроса ); $cf7_forms = get_posts($args); $cf7_scheme_args = array ( 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC', 'post_type' => 'wpcf7_contact_form', 'post_status' => 'publish', 'suppress_filters' => false, // подавление работы фильтров изменения SQL запроса 'meta_query' => array( 'relation' => 'EXISTS', array( 'key' => 'cf7cstmzr_style_scheme', ) ) ); $forms_group_by_style_scheme = Cf7_Style_Scheme::get_forms_group_by_style_scheme(); $current_url_state = get_site_url() . '/wp-admin/admin.php?page=cf7cstmzr_page'; if (!empty($_GET['tab'])) { $current_url_state .= '&tab=' . filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); } if (!empty($_GET['style_scheme'])) { $current_url_state .= '&style_scheme=' . filter_input( INPUT_GET, 'style_scheme', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); } $permalink_structure = get_option('permalink_structure'); ?>
>

> <body> tag', 'cf7-styler') ?>

<body> tag', 'cf7-styler') ?>


> > > > > > > > >
>
>
>
>

>
>
>
>
>
>

style="display: inline-block;"> ID); } else { echo __('Contact form 7 list', 'cf7-styler'); } // if ($show_description) { if (true) { ?>

> > %s, you can style it with current Style scheme.', 'cf7-styler' ), $first_form_scheme_title ); ?> > %s globally, you can style it with current Style scheme.', 'cf7-styler' ), $enabled_globally_title ); ?> >

>
$styled_form_style) { $styled_form = get_post($styled_id); if (!empty($styled_form)) { $styled_form_id = $styled_id; $styled_form_title = $styled_form->post_title; $styled_form_style_title = $style_schemes[$styled_form_style]['title']; $styled_form_style_slug = $styled_form_style; } } } ?>
> %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.', 'cf7-styler' ), $styled_form_title, $styled_form_style_title ); ?> >

first Contact Form 7', 'cf7-styler') ?>

ID; if (!empty($cache_form)) { $cf7_form_id = $cache_form; } if (false) { ?>

first Contact Form 7', 'cf7-styler') ?>

admin/partials/cf7-customizer-admin-preview-mode.php000064400000005253147600046700016545 0ustar00
: > >
admin/partials/cf7-customizer-admin-display.php000064400000004606147600046700015610 0ustar00

Knowledge Base page for fixing possible issues', 'cf7-styler'); ?>

Enter your WP2LEADS pro license or get a license here!', 'cf7-styler'); ?>

is_trial_utilized()) { ?>

here!', 'cf7-styler'); ?>

admin/vendors/codemirror.js000064400001405524147600046700012036 0ustar00// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE // This is CodeMirror (https://codemirror.net), a code editor // implemented in JavaScript on top of the browser's DOM. // // You can find some technical background for some of the code below // at http://marijnhaverbeke.nl/blog/#cm-internals . (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.CodeMirror = factory()); }(this, (function () { 'use strict'; // Kludges for bugs and behavior differences that can't be feature // detected are enabled based on userAgent etc sniffing. var userAgent = navigator.userAgent; var platform = navigator.platform; var gecko = /gecko\/\d/i.test(userAgent); var ie_upto10 = /MSIE \d/.test(userAgent); var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); var edge = /Edge\/(\d+)/.exec(userAgent); var ie = ie_upto10 || ie_11up || edge; var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); var webkit = !edge && /WebKit\//.test(userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); var chrome = !edge && /Chrome\//.test(userAgent); var presto = /Opera\//.test(userAgent); var safari = /Apple Computer/.test(navigator.vendor); var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); var phantom = /PhantomJS/.test(userAgent); var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); var android = /Android/.test(userAgent); // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); var mac = ios || /Mac/.test(platform); var chromeOS = /\bCrOS\b/.test(userAgent); var windows = /win/i.test(platform); var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); if (presto_version) { presto_version = Number(presto_version[1]); } if (presto_version && presto_version >= 15) { presto = false; webkit = true; } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); var captureRightClick = gecko || (ie && ie_version >= 9); function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } var rmClass = function(node, cls) { var current = node.className; var match = classTest(cls).exec(current); if (match) { var after = current.slice(match.index + match[0].length); node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); } }; function removeChildren(e) { for (var count = e.childNodes.length; count > 0; --count) { e.removeChild(e.firstChild); } return e } function removeChildrenAndAdd(parent, e) { return removeChildren(parent).appendChild(e) } function elt(tag, content, className, style) { var e = document.createElement(tag); if (className) { e.className = className; } if (style) { e.style.cssText = style; } if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } return e } // wrapper for elt, which removes the elt from the accessibility tree function eltP(tag, content, className, style) { var e = elt(tag, content, className, style); e.setAttribute("role", "presentation"); return e } var range; if (document.createRange) { range = function(node, start, end, endNode) { var r = document.createRange(); r.setEnd(endNode || node, end); r.setStart(node, start); return r }; } else { range = function(node, start, end) { var r = document.body.createTextRange(); try { r.moveToElementText(node.parentNode); } catch(e) { return r } r.collapse(true); r.moveEnd("character", end); r.moveStart("character", start); return r }; } function contains(parent, child) { if (child.nodeType == 3) // Android browser always returns false when child is a textnode { child = child.parentNode; } if (parent.contains) { return parent.contains(child) } do { if (child.nodeType == 11) { child = child.host; } if (child == parent) { return true } } while (child = child.parentNode) } function activeElt() { // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. // IE < 10 will throw when accessed while the page is loading or in an iframe. // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. var activeElement; try { activeElement = document.activeElement; } catch(e) { activeElement = document.body || null; } while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) { activeElement = activeElement.shadowRoot.activeElement; } return activeElement } function addClass(node, cls) { var current = node.className; if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } } function joinClasses(a, b) { var as = a.split(" "); for (var i = 0; i < as.length; i++) { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } return b } var selectInput = function(node) { node.select(); }; if (ios) // Mobile Safari apparently has a bug where select() is broken. { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } else if (ie) // Suppress mysterious IE10 errors { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } function bind(f) { var args = Array.prototype.slice.call(arguments, 1); return function(){return f.apply(null, args)} } function copyObj(obj, target, overwrite) { if (!target) { target = {}; } for (var prop in obj) { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) { target[prop] = obj[prop]; } } return target } // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. function countColumn(string, end, tabSize, startIndex, startValue) { if (end == null) { end = string.search(/[^\s\u00a0]/); if (end == -1) { end = string.length; } } for (var i = startIndex || 0, n = startValue || 0;;) { var nextTab = string.indexOf("\t", i); if (nextTab < 0 || nextTab >= end) { return n + (end - i) } n += nextTab - i; n += tabSize - (n % tabSize); i = nextTab + 1; } } var Delayed = function() { this.id = null; this.f = null; this.time = 0; this.handler = bind(this.onTimeout, this); }; Delayed.prototype.onTimeout = function (self) { self.id = 0; if (self.time <= +new Date) { self.f(); } else { setTimeout(self.handler, self.time - +new Date); } }; Delayed.prototype.set = function (ms, f) { this.f = f; var time = +new Date + ms; if (!this.id || time < this.time) { clearTimeout(this.id); this.id = setTimeout(this.handler, ms); this.time = time; } }; function indexOf(array, elt) { for (var i = 0; i < array.length; ++i) { if (array[i] == elt) { return i } } return -1 } // Number of pixels added to scroller and sizer to hide scrollbar var scrollerGap = 30; // Returned or thrown by various protocols to signal 'I'm not // handling this'. var Pass = {toString: function(){return "CodeMirror.Pass"}}; // Reused option objects for setSelection & friends var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; // The inverse of countColumn -- find the offset that corresponds to // a particular column. function findColumn(string, goal, tabSize) { for (var pos = 0, col = 0;;) { var nextTab = string.indexOf("\t", pos); if (nextTab == -1) { nextTab = string.length; } var skipped = nextTab - pos; if (nextTab == string.length || col + skipped >= goal) { return pos + Math.min(skipped, goal - col) } col += nextTab - pos; col += tabSize - (col % tabSize); pos = nextTab + 1; if (col >= goal) { return pos } } } var spaceStrs = [""]; function spaceStr(n) { while (spaceStrs.length <= n) { spaceStrs.push(lst(spaceStrs) + " "); } return spaceStrs[n] } function lst(arr) { return arr[arr.length-1] } function map(array, f) { var out = []; for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } return out } function insertSorted(array, value, score) { var pos = 0, priority = score(value); while (pos < array.length && score(array[pos]) <= priority) { pos++; } array.splice(pos, 0, value); } function nothing() {} function createObj(base, props) { var inst; if (Object.create) { inst = Object.create(base); } else { nothing.prototype = base; inst = new nothing(); } if (props) { copyObj(props, inst); } return inst } var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; function isWordCharBasic(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) } function isWordChar(ch, helper) { if (!helper) { return isWordCharBasic(ch) } if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } return helper.test(ch) } function isEmpty(obj) { for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } return true } // Extending unicode characters. A series of a non-extending char + // any number of extending chars is treated as a single unit as far // as editing and measuring is concerned. This is not fully correct, // since some scripts/fonts/browsers also treat other configurations // of code points as a group. var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. function skipExtendingChars(str, pos, dir) { while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } return pos } // Returns the value from the range [`from`; `to`] that satisfies // `pred` and is closest to `from`. Assumes that at least `to` // satisfies `pred`. Supports `from` being greater than `to`. function findFirst(pred, from, to) { // At any point we are certain `to` satisfies `pred`, don't know // whether `from` does. var dir = from > to ? -1 : 1; for (;;) { if (from == to) { return from } var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); if (mid == from) { return pred(mid) ? from : to } if (pred(mid)) { to = mid; } else { from = mid + dir; } } } // BIDI HELPERS function iterateBidiSections(order, from, to, f) { if (!order) { return f(from, to, "ltr", 0) } var found = false; for (var i = 0; i < order.length; ++i) { var part = order[i]; if (part.from < to && part.to > from || from == to && part.to == from) { f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); found = true; } } if (!found) { f(from, to, "ltr"); } } var bidiOther = null; function getBidiPartAt(order, ch, sticky) { var found; bidiOther = null; for (var i = 0; i < order.length; ++i) { var cur = order[i]; if (cur.from < ch && cur.to > ch) { return i } if (cur.to == ch) { if (cur.from != cur.to && sticky == "before") { found = i; } else { bidiOther = i; } } if (cur.from == ch) { if (cur.from != cur.to && sticky != "before") { found = i; } else { bidiOther = i; } } } return found != null ? found : bidiOther } // Bidirectional ordering algorithm // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm // that this (partially) implements. // One-char codes used for character types: // L (L): Left-to-Right // R (R): Right-to-Left // r (AL): Right-to-Left Arabic // 1 (EN): European Number // + (ES): European Number Separator // % (ET): European Number Terminator // n (AN): Arabic Number // , (CS): Common Number Separator // m (NSM): Non-Spacing Mark // b (BN): Boundary Neutral // s (B): Paragraph Separator // t (S): Segment Separator // w (WS): Whitespace // N (ON): Other Neutrals // Returns null if characters are ordered as they appear // (left-to-right), or an array of sections ({from, to, level} // objects) in the order in which they occur visually. var bidiOrdering = (function() { // Character types for codepoints 0 to 0xff var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; // Character types for codepoints 0x600 to 0x6f9 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; function charType(code) { if (code <= 0xf7) { return lowTypes.charAt(code) } else if (0x590 <= code && code <= 0x5f4) { return "R" } else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } else if (0x6ee <= code && code <= 0x8ac) { return "r" } else if (0x2000 <= code && code <= 0x200b) { return "w" } else if (code == 0x200c) { return "b" } else { return "L" } } var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; function BidiSpan(level, from, to) { this.level = level; this.from = from; this.to = to; } return function(str, direction) { var outerType = direction == "ltr" ? "L" : "R"; if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } var len = str.length, types = []; for (var i = 0; i < len; ++i) { types.push(charType(str.charCodeAt(i))); } // W1. Examine each non-spacing mark (NSM) in the level run, and // change the type of the NSM to the type of the previous // character. If the NSM is at the start of the level run, it will // get the type of sor. for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { var type = types[i$1]; if (type == "m") { types[i$1] = prev; } else { prev = type; } } // W2. Search backwards from each instance of a European number // until the first strong type (R, L, AL, or sor) is found. If an // AL is found, change the type of the European number to Arabic // number. // W3. Change all ALs to R. for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { var type$1 = types[i$2]; if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } } // W4. A single European separator between two European numbers // changes to a European number. A single common separator between // two numbers of the same type changes to that type. for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { var type$2 = types[i$3]; if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } else if (type$2 == "," && prev$1 == types[i$3+1] && (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } prev$1 = type$2; } // W5. A sequence of European terminators adjacent to European // numbers changes to all European numbers. // W6. Otherwise, separators and terminators change to Other // Neutral. for (var i$4 = 0; i$4 < len; ++i$4) { var type$3 = types[i$4]; if (type$3 == ",") { types[i$4] = "N"; } else if (type$3 == "%") { var end = (void 0); for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; for (var j = i$4; j < end; ++j) { types[j] = replace; } i$4 = end - 1; } } // W7. Search backwards from each instance of a European number // until the first strong type (R, L, or sor) is found. If an L is // found, then change the type of the European number to L. for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { var type$4 = types[i$5]; if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } else if (isStrong.test(type$4)) { cur$1 = type$4; } } // N1. A sequence of neutrals takes the direction of the // surrounding strong text if the text on both sides has the same // direction. European and Arabic numbers act as if they were R in // terms of their influence on neutrals. Start-of-level-run (sor) // and end-of-level-run (eor) are used at level run boundaries. // N2. Any remaining neutrals take the embedding direction. for (var i$6 = 0; i$6 < len; ++i$6) { if (isNeutral.test(types[i$6])) { var end$1 = (void 0); for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} var before = (i$6 ? types[i$6-1] : outerType) == "L"; var after = (end$1 < len ? types[end$1] : outerType) == "L"; var replace$1 = before == after ? (before ? "L" : "R") : outerType; for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } i$6 = end$1 - 1; } } // Here we depart from the documented algorithm, in order to avoid // building up an actual levels array. Since there are only three // levels (0, 1, 2) in an implementation that doesn't take // explicit embedding into account, we can build up the order on // the fly, without following the level-based algorithm. var order = [], m; for (var i$7 = 0; i$7 < len;) { if (countsAsLeft.test(types[i$7])) { var start = i$7; for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} order.push(new BidiSpan(0, start, i$7)); } else { var pos = i$7, at = order.length; for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} for (var j$2 = pos; j$2 < i$7;) { if (countsAsNum.test(types[j$2])) { if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } var nstart = j$2; for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} order.splice(at, 0, new BidiSpan(2, nstart, j$2)); pos = j$2; } else { ++j$2; } } if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } } } if (direction == "ltr") { if (order[0].level == 1 && (m = str.match(/^\s+/))) { order[0].from = m[0].length; order.unshift(new BidiSpan(0, 0, m[0].length)); } if (lst(order).level == 1 && (m = str.match(/\s+$/))) { lst(order).to -= m[0].length; order.push(new BidiSpan(0, len - m[0].length, len)); } } return direction == "rtl" ? order.reverse() : order } })(); // Get the bidi ordering for the given line (and cache it). Returns // false for lines that are fully left-to-right, and an array of // BidiSpan objects otherwise. function getOrder(line, direction) { var order = line.order; if (order == null) { order = line.order = bidiOrdering(line.text, direction); } return order } // EVENT HANDLING // Lightweight event framework. on/off also work on DOM nodes, // registering native DOM handlers. var noHandlers = []; var on = function(emitter, type, f) { if (emitter.addEventListener) { emitter.addEventListener(type, f, false); } else if (emitter.attachEvent) { emitter.attachEvent("on" + type, f); } else { var map$$1 = emitter._handlers || (emitter._handlers = {}); map$$1[type] = (map$$1[type] || noHandlers).concat(f); } }; function getHandlers(emitter, type) { return emitter._handlers && emitter._handlers[type] || noHandlers } function off(emitter, type, f) { if (emitter.removeEventListener) { emitter.removeEventListener(type, f, false); } else if (emitter.detachEvent) { emitter.detachEvent("on" + type, f); } else { var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]; if (arr) { var index = indexOf(arr, f); if (index > -1) { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } } } } function signal(emitter, type /*, values...*/) { var handlers = getHandlers(emitter, type); if (!handlers.length) { return } var args = Array.prototype.slice.call(arguments, 2); for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } } // The DOM events that CodeMirror handles can be overridden by // registering a (non-DOM) handler on the editor for the event name, // and preventDefault-ing the event in that handler. function signalDOMEvent(cm, e, override) { if (typeof e == "string") { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } signal(cm, override || e.type, cm, e); return e_defaultPrevented(e) || e.codemirrorIgnore } function signalCursorActivity(cm) { var arr = cm._handlers && cm._handlers.cursorActivity; if (!arr) { return } var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) { set.push(arr[i]); } } } function hasHandler(emitter, type) { return getHandlers(emitter, type).length > 0 } // Add on and off methods to a constructor's prototype, to make // registering events on such objects more convenient. function eventMixin(ctor) { ctor.prototype.on = function(type, f) {on(this, type, f);}; ctor.prototype.off = function(type, f) {off(this, type, f);}; } // Due to the fact that we still support jurassic IE versions, some // compatibility wrappers are needed. function e_preventDefault(e) { if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } } function e_stopPropagation(e) { if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } function e_defaultPrevented(e) { return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false } function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} function e_target(e) {return e.target || e.srcElement} function e_button(e) { var b = e.which; if (b == null) { if (e.button & 1) { b = 1; } else if (e.button & 2) { b = 3; } else if (e.button & 4) { b = 2; } } if (mac && e.ctrlKey && b == 1) { b = 3; } return b } // Detect drag-and-drop var dragAndDrop = function() { // There is *some* kind of drag-and-drop support in IE6-8, but I // couldn't get it to work yet. if (ie && ie_version < 9) { return false } var div = elt('div'); return "draggable" in div || "dragDrop" in div }(); var zwspSupported; function zeroWidthElement(measure) { if (zwspSupported == null) { var test = elt("span", "\u200b"); removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); if (measure.firstChild.offsetHeight != 0) { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } } var node = zwspSupported ? elt("span", "\u200b") : elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); node.setAttribute("cm-text", ""); return node } // Feature-detect IE's crummy client rect reporting for bidi text var badBidiRects; function hasBadBidiRects(measure) { if (badBidiRects != null) { return badBidiRects } var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); var r0 = range(txt, 0, 1).getBoundingClientRect(); var r1 = range(txt, 1, 2).getBoundingClientRect(); removeChildren(measure); if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) return badBidiRects = (r1.right - r0.right < 3) } // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { var pos = 0, result = [], l = string.length; while (pos <= l) { var nl = string.indexOf("\n", pos); if (nl == -1) { nl = string.length; } var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); var rt = line.indexOf("\r"); if (rt != -1) { result.push(line.slice(0, rt)); pos += rt + 1; } else { result.push(line); pos = nl + 1; } } return result } : function (string) { return string.split(/\r\n?|\n/); }; var hasSelection = window.getSelection ? function (te) { try { return te.selectionStart != te.selectionEnd } catch(e) { return false } } : function (te) { var range$$1; try {range$$1 = te.ownerDocument.selection.createRange();} catch(e) {} if (!range$$1 || range$$1.parentElement() != te) { return false } return range$$1.compareEndPoints("StartToEnd", range$$1) != 0 }; var hasCopyEvent = (function () { var e = elt("div"); if ("oncopy" in e) { return true } e.setAttribute("oncopy", "return;"); return typeof e.oncopy == "function" })(); var badZoomedRects = null; function hasBadZoomedRects(measure) { if (badZoomedRects != null) { return badZoomedRects } var node = removeChildrenAndAdd(measure, elt("span", "x")); var normal = node.getBoundingClientRect(); var fromRange = range(node, 0, 1).getBoundingClientRect(); return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 } // Known modes, by name and by MIME var modes = {}, mimeModes = {}; // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) function defineMode(name, mode) { if (arguments.length > 2) { mode.dependencies = Array.prototype.slice.call(arguments, 2); } modes[name] = mode; } function defineMIME(mime, spec) { mimeModes[mime] = spec; } // Given a MIME type, a {name, ...options} config object, or a name // string, return a mode config object. function resolveMode(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { var found = mimeModes[spec.name]; if (typeof found == "string") { found = {name: found}; } spec = createObj(found, spec); spec.name = found.name; } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { return resolveMode("application/xml") } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { return resolveMode("application/json") } if (typeof spec == "string") { return {name: spec} } else { return spec || {name: "null"} } } // Given a mode spec (anything that resolveMode accepts), find and // initialize an actual mode object. function getMode(options, spec) { spec = resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) { return getMode(options, "text/plain") } var modeObj = mfactory(options, spec); if (modeExtensions.hasOwnProperty(spec.name)) { var exts = modeExtensions[spec.name]; for (var prop in exts) { if (!exts.hasOwnProperty(prop)) { continue } if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } modeObj[prop] = exts[prop]; } } modeObj.name = spec.name; if (spec.helperType) { modeObj.helperType = spec.helperType; } if (spec.modeProps) { for (var prop$1 in spec.modeProps) { modeObj[prop$1] = spec.modeProps[prop$1]; } } return modeObj } // This can be used to attach properties to mode objects from // outside the actual mode definition. var modeExtensions = {}; function extendMode(mode, properties) { var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); copyObj(properties, exts); } function copyState(mode, state) { if (state === true) { return state } if (mode.copyState) { return mode.copyState(state) } var nstate = {}; for (var n in state) { var val = state[n]; if (val instanceof Array) { val = val.concat([]); } nstate[n] = val; } return nstate } // Given a mode and a state (for that mode), find the inner mode and // state at the position that the state refers to. function innerMode(mode, state) { var info; while (mode.innerMode) { info = mode.innerMode(state); if (!info || info.mode == mode) { break } state = info.state; mode = info.mode; } return info || {mode: mode, state: state} } function startState(mode, a1, a2) { return mode.startState ? mode.startState(a1, a2) : true } // STRING STREAM // Fed to the mode parsers, provides helper functions to make // parsers more succinct. var StringStream = function(string, tabSize, lineOracle) { this.pos = this.start = 0; this.string = string; this.tabSize = tabSize || 8; this.lastColumnPos = this.lastColumnValue = 0; this.lineStart = 0; this.lineOracle = lineOracle; }; StringStream.prototype.eol = function () {return this.pos >= this.string.length}; StringStream.prototype.sol = function () {return this.pos == this.lineStart}; StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; StringStream.prototype.next = function () { if (this.pos < this.string.length) { return this.string.charAt(this.pos++) } }; StringStream.prototype.eat = function (match) { var ch = this.string.charAt(this.pos); var ok; if (typeof match == "string") { ok = ch == match; } else { ok = ch && (match.test ? match.test(ch) : match(ch)); } if (ok) {++this.pos; return ch} }; StringStream.prototype.eatWhile = function (match) { var start = this.pos; while (this.eat(match)){} return this.pos > start }; StringStream.prototype.eatSpace = function () { var this$1 = this; var start = this.pos; while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; } return this.pos > start }; StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; StringStream.prototype.skipTo = function (ch) { var found = this.string.indexOf(ch, this.pos); if (found > -1) {this.pos = found; return true} }; StringStream.prototype.backUp = function (n) {this.pos -= n;}; StringStream.prototype.column = function () { if (this.lastColumnPos < this.start) { this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); this.lastColumnPos = this.start; } return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }; StringStream.prototype.indentation = function () { return countColumn(this.string, null, this.tabSize) - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }; StringStream.prototype.match = function (pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; var substr = this.string.substr(this.pos, pattern.length); if (cased(substr) == cased(pattern)) { if (consume !== false) { this.pos += pattern.length; } return true } } else { var match = this.string.slice(this.pos).match(pattern); if (match && match.index > 0) { return null } if (match && consume !== false) { this.pos += match[0].length; } return match } }; StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; StringStream.prototype.hideFirstChars = function (n, inner) { this.lineStart += n; try { return inner() } finally { this.lineStart -= n; } }; StringStream.prototype.lookAhead = function (n) { var oracle = this.lineOracle; return oracle && oracle.lookAhead(n) }; StringStream.prototype.baseToken = function () { var oracle = this.lineOracle; return oracle && oracle.baseToken(this.pos) }; // Find the line object corresponding to the given line number. function getLine(doc, n) { n -= doc.first; if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } var chunk = doc; while (!chunk.lines) { for (var i = 0;; ++i) { var child = chunk.children[i], sz = child.chunkSize(); if (n < sz) { chunk = child; break } n -= sz; } } return chunk.lines[n] } // Get the part of a document between two positions, as an array of // strings. function getBetween(doc, start, end) { var out = [], n = start.line; doc.iter(start.line, end.line + 1, function (line) { var text = line.text; if (n == end.line) { text = text.slice(0, end.ch); } if (n == start.line) { text = text.slice(start.ch); } out.push(text); ++n; }); return out } // Get the lines between from and to, as array of strings. function getLines(doc, from, to) { var out = []; doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value return out } // Update the height of a line, propagating the height change // upwards to parent nodes. function updateLineHeight(line, height) { var diff = height - line.height; if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } } // Given a line object, find its line number by walking up through // its parent links. function lineNo(line) { if (line.parent == null) { return null } var cur = line.parent, no = indexOf(cur.lines, line); for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { for (var i = 0;; ++i) { if (chunk.children[i] == cur) { break } no += chunk.children[i].chunkSize(); } } return no + cur.first } // Find the line at the given vertical position, using the height // information in the document tree. function lineAtHeight(chunk, h) { var n = chunk.first; outer: do { for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { var child = chunk.children[i$1], ch = child.height; if (h < ch) { chunk = child; continue outer } h -= ch; n += child.chunkSize(); } return n } while (!chunk.lines) var i = 0; for (; i < chunk.lines.length; ++i) { var line = chunk.lines[i], lh = line.height; if (h < lh) { break } h -= lh; } return n + i } function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} function lineNumberFor(options, i) { return String(options.lineNumberFormatter(i + options.firstLineNumber)) } // A Pos instance represents a position within the text. function Pos(line, ch, sticky) { if ( sticky === void 0 ) sticky = null; if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } this.line = line; this.ch = ch; this.sticky = sticky; } // Compare two positions, return 0 if they are the same, a negative // number when a is less, and a positive number otherwise. function cmp(a, b) { return a.line - b.line || a.ch - b.ch } function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } function copyPos(x) {return Pos(x.line, x.ch)} function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } function minPos(a, b) { return cmp(a, b) < 0 ? a : b } // Most of the external API clips given positions to make sure they // actually exist within the document. function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} function clipPos(doc, pos) { if (pos.line < doc.first) { return Pos(doc.first, 0) } var last = doc.first + doc.size - 1; if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } return clipToLen(pos, getLine(doc, pos.line).text.length) } function clipToLen(pos, linelen) { var ch = pos.ch; if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } else if (ch < 0) { return Pos(pos.line, 0) } else { return pos } } function clipPosArray(doc, array) { var out = []; for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } return out } var SavedContext = function(state, lookAhead) { this.state = state; this.lookAhead = lookAhead; }; var Context = function(doc, state, line, lookAhead) { this.state = state; this.doc = doc; this.line = line; this.maxLookAhead = lookAhead || 0; this.baseTokens = null; this.baseTokenPos = 1; }; Context.prototype.lookAhead = function (n) { var line = this.doc.getLine(this.line + n); if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } return line }; Context.prototype.baseToken = function (n) { var this$1 = this; if (!this.baseTokens) { return null } while (this.baseTokens[this.baseTokenPos] <= n) { this$1.baseTokenPos += 2; } var type = this.baseTokens[this.baseTokenPos + 1]; return {type: type && type.replace(/( |^)overlay .*/, ""), size: this.baseTokens[this.baseTokenPos] - n} }; Context.prototype.nextLine = function () { this.line++; if (this.maxLookAhead > 0) { this.maxLookAhead--; } }; Context.fromSaved = function (doc, saved, line) { if (saved instanceof SavedContext) { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } else { return new Context(doc, copyState(doc.mode, saved), line) } }; Context.prototype.save = function (copy) { var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state }; // Compute a style array (an array starting with a mode generation // -- for invalidation -- followed by pairs of end positions and // style strings), which is used to highlight the tokens on the // line. function highlightLine(cm, line, context, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). var st = [cm.state.modeGen], lineClasses = {}; // Compute the base array of styles runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, lineClasses, forceToEnd); var state = context.state; // Run overlays, adjust style array. var loop = function ( o ) { context.baseTokens = st; var overlay = cm.state.overlays[o], i = 1, at = 0; context.state = true; runMode(cm, line.text, overlay.mode, context, function (end, style) { var start = i; // Ensure there's a token end at the current position, and that i points at it while (at < end) { var i_end = st[i]; if (i_end > end) { st.splice(i, 1, end, st[i+1], i_end); } i += 2; at = Math.min(end, i_end); } if (!style) { return } if (overlay.opaque) { st.splice(start, i - start, end, "overlay " + style); i = start + 2; } else { for (; start < i; start += 2) { var cur = st[start+1]; st[start+1] = (cur ? cur + " " : "") + "overlay " + style; } } }, lineClasses); context.state = state; context.baseTokens = null; context.baseTokenPos = 1; }; for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} } function getLineStyles(cm, line, updateFrontier) { if (!line.styles || line.styles[0] != cm.state.modeGen) { var context = getContextBefore(cm, lineNo(line)); var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); var result = highlightLine(cm, line, context); if (resetState) { context.state = resetState; } line.stateAfter = context.save(!resetState); line.styles = result.styles; if (result.classes) { line.styleClasses = result.classes; } else if (line.styleClasses) { line.styleClasses = null; } if (updateFrontier === cm.doc.highlightFrontier) { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } } return line.styles } function getContextBefore(cm, n, precise) { var doc = cm.doc, display = cm.display; if (!doc.mode.startState) { return new Context(doc, true, n) } var start = findStartLine(cm, n, precise); var saved = start > doc.first && getLine(doc, start - 1).stateAfter; var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); doc.iter(start, n, function (line) { processLine(cm, line.text, context); var pos = context.line; line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; context.nextLine(); }); if (precise) { doc.modeFrontier = context.line; } return context } // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. Used for lines that // aren't currently visible. function processLine(cm, text, context, startAt) { var mode = cm.doc.mode; var stream = new StringStream(text, cm.options.tabSize, context); stream.start = stream.pos = startAt || 0; if (text == "") { callBlankLine(mode, context.state); } while (!stream.eol()) { readToken(mode, stream, context.state); stream.start = stream.pos; } } function callBlankLine(mode, state) { if (mode.blankLine) { return mode.blankLine(state) } if (!mode.innerMode) { return } var inner = innerMode(mode, state); if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } } function readToken(mode, stream, state, inner) { for (var i = 0; i < 10; i++) { if (inner) { inner[0] = innerMode(mode, state).mode; } var style = mode.token(stream, state); if (stream.pos > stream.start) { return style } } throw new Error("Mode " + mode.name + " failed to advance stream.") } var Token = function(stream, type, state) { this.start = stream.start; this.end = stream.pos; this.string = stream.current(); this.type = type || null; this.state = state; }; // Utility for getTokenAt and getLineTokens function takeToken(cm, pos, precise, asArray) { var doc = cm.doc, mode = doc.mode, style; pos = clipPos(doc, pos); var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; if (asArray) { tokens = []; } while ((asArray || stream.pos < pos.ch) && !stream.eol()) { stream.start = stream.pos; style = readToken(mode, stream, context.state); if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } } return asArray ? tokens : new Token(stream, style, context.state) } function extractLineClasses(type, output) { if (type) { for (;;) { var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); if (!lineClass) { break } type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); var prop = lineClass[1] ? "bgClass" : "textClass"; if (output[prop] == null) { output[prop] = lineClass[2]; } else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) { output[prop] += " " + lineClass[2]; } } } return type } // Run the given mode's parser over a line, calling f for each token. function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { var flattenSpans = mode.flattenSpans; if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } var curStart = 0, curStyle = null; var stream = new StringStream(text, cm.options.tabSize, context), style; var inner = cm.options.addModeClass && [null]; if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false; if (forceToEnd) { processLine(cm, text, context, stream.pos); } stream.pos = text.length; style = null; } else { style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); } if (inner) { var mName = inner[0].name; if (mName) { style = "m-" + (style ? mName + " " + style : mName); } } if (!flattenSpans || curStyle != style) { while (curStart < stream.start) { curStart = Math.min(stream.start, curStart + 5000); f(curStart, curStyle); } curStyle = style; } stream.start = stream.pos; } while (curStart < stream.pos) { // Webkit seems to refuse to render text nodes longer than 57444 // characters, and returns inaccurate measurements in nodes // starting around 5000 chars. var pos = Math.min(stream.pos, curStart + 5000); f(pos, curStyle); curStart = pos; } } // Finds the line to start with when starting a parse. Tries to // find a line with a stateAfter, so that it can start with a // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { var minindent, minline, doc = cm.doc; var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); for (var search = n; search > lim; --search) { if (search <= doc.first) { return doc.first } var line = getLine(doc, search - 1), after = line.stateAfter; if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) { return search } var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { minline = search - 1; minindent = indented; } } return minline } function retreatFrontier(doc, n) { doc.modeFrontier = Math.min(doc.modeFrontier, n); if (doc.highlightFrontier < n - 10) { return } var start = doc.first; for (var line = n - 1; line > start; line--) { var saved = getLine(doc, line).stateAfter; // change is on 3 // state on line 1 looked ahead 2 -- so saw 3 // test 1 + 2 < 3 should cover this if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { start = line + 1; break } } doc.highlightFrontier = Math.min(doc.highlightFrontier, start); } // Optimize some code when these features are not used. var sawReadOnlySpans = false, sawCollapsedSpans = false; function seeReadOnlySpans() { sawReadOnlySpans = true; } function seeCollapsedSpans() { sawCollapsedSpans = true; } // TEXTMARKER SPANS function MarkedSpan(marker, from, to) { this.marker = marker; this.from = from; this.to = to; } // Search an array of spans for a span matching the given marker. function getMarkedSpanFor(spans, marker) { if (spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.marker == marker) { return span } } } } // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). function removeMarkedSpan(spans, span) { var r; for (var i = 0; i < spans.length; ++i) { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } return r } // Add a span to a line. function addMarkedSpan(line, span) { line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; span.marker.attachLine(line); } // Used for the algorithm that adjusts markers for a change in the // document. These functions cut an array of spans at a given // character position, returning an array of remaining chunks (or // undefined if nothing remains). function markedSpansBefore(old, startCh, isInsert) { var nw; if (old) { for (var i = 0; i < old.length; ++i) { var span = old[i], marker = span.marker; var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); } } } return nw } function markedSpansAfter(old, endCh, isInsert) { var nw; if (old) { for (var i = 0; i < old.length; ++i) { var span = old[i], marker = span.marker; var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, span.to == null ? null : span.to - endCh)); } } } return nw } // Given a change object, compute the new set of marker spans that // cover the line in which the change took place. Removes spans // entirely within the change, reconnects spans belonging to the // same marker that appear on both sides of the change, and cuts off // spans partially within the change. Returns an array of span // arrays with one element for each line in (after) the change. function stretchSpansOverChange(doc, change) { if (change.full) { return null } var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; if (!oldFirst && !oldLast) { return null } var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; // Get the spans that 'stick out' on both sides var first = markedSpansBefore(oldFirst, startCh, isInsert); var last = markedSpansAfter(oldLast, endCh, isInsert); // Next, merge those two ends var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); if (first) { // Fix up .to properties of first for (var i = 0; i < first.length; ++i) { var span = first[i]; if (span.to == null) { var found = getMarkedSpanFor(last, span.marker); if (!found) { span.to = startCh; } else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } } } } if (last) { // Fix up .from in last (or move them into first in case of sameLine) for (var i$1 = 0; i$1 < last.length; ++i$1) { var span$1 = last[i$1]; if (span$1.to != null) { span$1.to += offset; } if (span$1.from == null) { var found$1 = getMarkedSpanFor(first, span$1.marker); if (!found$1) { span$1.from = offset; if (sameLine) { (first || (first = [])).push(span$1); } } } else { span$1.from += offset; if (sameLine) { (first || (first = [])).push(span$1); } } } } // Make sure we didn't create any zero-length spans if (first) { first = clearEmptySpans(first); } if (last && last != first) { last = clearEmptySpans(last); } var newMarkers = [first]; if (!sameLine) { // Fill gap with whole-line-spans var gap = change.text.length - 2, gapMarkers; if (gap > 0 && first) { for (var i$2 = 0; i$2 < first.length; ++i$2) { if (first[i$2].to == null) { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } for (var i$3 = 0; i$3 < gap; ++i$3) { newMarkers.push(gapMarkers); } newMarkers.push(last); } return newMarkers } // Remove spans that are empty and don't have a clearWhenEmpty // option of false. function clearEmptySpans(spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) { spans.splice(i--, 1); } } if (!spans.length) { return null } return spans } // Used to 'clip' out readOnly ranges when making a change. function removeReadOnlyRanges(doc, from, to) { var markers = null; doc.iter(from.line, to.line + 1, function (line) { if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var mark = line.markedSpans[i].marker; if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) { (markers || (markers = [])).push(mark); } } } }); if (!markers) { return null } var parts = [{from: from, to: to}]; for (var i = 0; i < markers.length; ++i) { var mk = markers[i], m = mk.find(0); for (var j = 0; j < parts.length; ++j) { var p = parts[j]; if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) { newParts.push({from: p.from, to: m.from}); } if (dto > 0 || !mk.inclusiveRight && !dto) { newParts.push({from: m.to, to: p.to}); } parts.splice.apply(parts, newParts); j += newParts.length - 3; } } return parts } // Connect or disconnect spans from a line. function detachMarkedSpans(line) { var spans = line.markedSpans; if (!spans) { return } for (var i = 0; i < spans.length; ++i) { spans[i].marker.detachLine(line); } line.markedSpans = null; } function attachMarkedSpans(line, spans) { if (!spans) { return } for (var i = 0; i < spans.length; ++i) { spans[i].marker.attachLine(line); } line.markedSpans = spans; } // Helpers used when computing which overlapping collapsed span // counts as the larger one. function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } // Returns a number indicating which of two overlapping collapsed // spans is larger (and thus includes the other). Falls back to // comparing ids when the spans cover exactly the same range. function compareCollapsedMarkers(a, b) { var lenDiff = a.lines.length - b.lines.length; if (lenDiff != 0) { return lenDiff } var aPos = a.find(), bPos = b.find(); var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); if (fromCmp) { return -fromCmp } var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); if (toCmp) { return toCmp } return b.id - a.id } // Find out whether a line ends or starts in a collapsed span. If // so, return the marker for that span. function collapsedSpanAtSide(line, start) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { sp = sps[i]; if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } } } return found } function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } function collapsedSpanAround(line, ch) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } } } return found } // Test whether there exists a collapsed span that partially // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) { var line = getLine(doc, lineNo$$1); var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (!sp.marker.collapsed) { continue } var found = sp.marker.find(0); var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) { return true } } } } // A visual line is a line as drawn on the screen. Folding, for // example, can cause multiple logical lines to appear on the same // visual line. This finds the start of the visual line that the // given line is part of (usually that is the line itself). function visualLine(line) { var merged; while (merged = collapsedSpanAtStart(line)) { line = merged.find(-1, true).line; } return line } function visualLineEnd(line) { var merged; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; } return line } // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. function visualLineContinued(line) { var merged, lines; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line ;(lines || (lines = [])).push(line); } return lines } // Get the line number of the start of the visual line that the // given line number is part of. function visualLineNo(doc, lineN) { var line = getLine(doc, lineN), vis = visualLine(line); if (line == vis) { return lineN } return lineNo(vis) } // Get the line number of the start of the next visual line after // the given line. function visualLineEndNo(doc, lineN) { if (lineN > doc.lastLine()) { return lineN } var line = getLine(doc, lineN), merged; if (!lineIsHidden(doc, line)) { return lineN } while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; } return lineNo(line) + 1 } // Compute whether a line is hidden. Lines count as hidden when they // are part of a visual line that starts with another line, or when // they are entirely covered by collapsed, non-widget span. function lineIsHidden(doc, line) { var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { sp = sps[i]; if (!sp.marker.collapsed) { continue } if (sp.from == null) { return true } if (sp.marker.widgetNode) { continue } if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) { return true } } } } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { var end = span.marker.find(1, true); return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) } if (span.marker.inclusiveRight && span.to == line.text.length) { return true } for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i]; if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && (sp.to == null || sp.to != span.from) && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && lineIsHiddenInner(doc, line, sp)) { return true } } } // Find the height above the given line. function heightAtLine(lineObj) { lineObj = visualLine(lineObj); var h = 0, chunk = lineObj.parent; for (var i = 0; i < chunk.lines.length; ++i) { var line = chunk.lines[i]; if (line == lineObj) { break } else { h += line.height; } } for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { for (var i$1 = 0; i$1 < p.children.length; ++i$1) { var cur = p.children[i$1]; if (cur == chunk) { break } else { h += cur.height; } } } return h } // Compute the character length of a line, taking into account // collapsed ranges (see markText) that might hide parts, and join // other lines onto it. function lineLength(line) { if (line.height == 0) { return 0 } var len = line.text.length, merged, cur = line; while (merged = collapsedSpanAtStart(cur)) { var found = merged.find(0, true); cur = found.from.line; len += found.from.ch - found.to.ch; } cur = line; while (merged = collapsedSpanAtEnd(cur)) { var found$1 = merged.find(0, true); len -= cur.text.length - found$1.from.ch; cur = found$1.to.line; len += cur.text.length - found$1.to.ch; } return len } // Find the longest line in the document. function findMaxLine(cm) { var d = cm.display, doc = cm.doc; d.maxLine = getLine(doc, doc.first); d.maxLineLength = lineLength(d.maxLine); d.maxLineChanged = true; doc.iter(function (line) { var len = lineLength(line); if (len > d.maxLineLength) { d.maxLineLength = len; d.maxLine = line; } }); } // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including // highlighting info (the styles array). var Line = function(text, markedSpans, estimateHeight) { this.text = text; attachMarkedSpans(this, markedSpans); this.height = estimateHeight ? estimateHeight(this) : 1; }; Line.prototype.lineNo = function () { return lineNo(this) }; eventMixin(Line); // Change the content (text, markers) of a line. Automatically // invalidates cached information and tries to re-estimate the // line's height. function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; if (line.stateAfter) { line.stateAfter = null; } if (line.styles) { line.styles = null; } if (line.order != null) { line.order = null; } detachMarkedSpans(line); attachMarkedSpans(line, markedSpans); var estHeight = estimateHeight ? estimateHeight(line) : 1; if (estHeight != line.height) { updateLineHeight(line, estHeight); } } // Detach a line from the document tree and its markers. function cleanUpLine(line) { line.parent = null; detachMarkedSpans(line); } // Convert a style as returned by a mode (either null, or a string // containing one or more styles) to a CSS style. This is cached, // and also looks for line-wide styles. var styleToClassCache = {}, styleToClassCacheWithMode = {}; function interpretTokenStyle(style, options) { if (!style || /^\s*$/.test(style)) { return null } var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; return cache[style] || (cache[style] = style.replace(/\S+/g, "cm-$&")) } // Render the DOM representation of the text of a line. Also builds // up a 'line map', which points at the DOM nodes that represent // specific stretches of text, and is used by the measuring code. // The returned object contains the DOM node, this map, and // information about line-wide styles that were set by the mode. function buildLineContent(cm, lineView) { // The padding-right forces the element to have a 'border', which // is needed on Webkit to be able to get line-level bounding // rectangles for it (in measureChar). var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, trailingSpace: false, splitSpaces: cm.getOption("lineWrapping")}; lineView.measure = {}; // Iterate over the logical lines that make up this visual line. for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); builder.pos = 0; builder.addToken = buildToken; // Optionally wire in some hacks into the token-rendering // algorithm, to deal with browser quirks. if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) { builder.addToken = buildTokenBadBidi(builder.addToken, order); } builder.map = []; var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); if (line.styleClasses) { if (line.styleClasses.bgClass) { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } if (line.styleClasses.textClass) { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } } // Ensure at least a single node is present, for measuring. if (builder.map.length == 0) { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } // Store the map and a cache object for the current logical line if (i == 0) { lineView.measure.map = builder.map; lineView.measure.cache = {}; } else { (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); } } // See issue #2901 if (webkit) { var last = builder.content.lastChild; if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) { builder.content.className = "cm-tab-wrap-hack"; } } signal(cm, "renderLine", cm, lineView.line, builder.pre); if (builder.pre.className) { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } return builder } function defaultSpecialCharPlaceholder(ch) { var token = elt("span", "\u2022", "cm-invalidchar"); token.title = "\\u" + ch.charCodeAt(0).toString(16); token.setAttribute("aria-label", token.title); return token } // Build up the DOM representation for a single token, and add it to // the line map. Takes care to render special characters separately. function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { if (!text) { return } var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; var special = builder.cm.state.specialChars, mustWrap = false; var content; if (!special.test(text)) { builder.col += text.length; content = document.createTextNode(displayText); builder.map.push(builder.pos, builder.pos + text.length, content); if (ie && ie_version < 9) { mustWrap = true; } builder.pos += text.length; } else { content = document.createDocumentFragment(); var pos = 0; while (true) { special.lastIndex = pos; var m = special.exec(text); var skipped = m ? m.index - pos : text.length - pos; if (skipped) { var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } else { content.appendChild(txt); } builder.map.push(builder.pos, builder.pos + skipped, txt); builder.col += skipped; builder.pos += skipped; } if (!m) { break } pos += skipped + 1; var txt$1 = (void 0); if (m[0] == "\t") { var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); txt$1.setAttribute("role", "presentation"); txt$1.setAttribute("cm-text", "\t"); builder.col += tabWidth; } else if (m[0] == "\r" || m[0] == "\n") { txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); txt$1.setAttribute("cm-text", m[0]); builder.col += 1; } else { txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); txt$1.setAttribute("cm-text", m[0]); if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } else { content.appendChild(txt$1); } builder.col += 1; } builder.map.push(builder.pos, builder.pos + 1, txt$1); builder.pos++; } } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; if (style || startStyle || endStyle || mustWrap || css) { var fullStyle = style || ""; if (startStyle) { fullStyle += startStyle; } if (endStyle) { fullStyle += endStyle; } var token = elt("span", [content], fullStyle, css); if (attributes) { for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") { token.setAttribute(attr, attributes[attr]); } } } return builder.content.appendChild(token) } builder.content.appendChild(content); } // Change some spaces to NBSP to prevent the browser from collapsing // trailing spaces at the end of a line when rendering text (issue #1362). function splitSpaces(text, trailingBefore) { if (text.length > 1 && !/ /.test(text)) { return text } var spaceBefore = trailingBefore, result = ""; for (var i = 0; i < text.length; i++) { var ch = text.charAt(i); if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) { ch = "\u00a0"; } result += ch; spaceBefore = ch == " "; } return result } // Work around nonsense dimensions being reported for stretches of // right-to-left text. function buildTokenBadBidi(inner, order) { return function (builder, text, style, startStyle, endStyle, css, attributes) { style = style ? style + " cm-force-border" : "cm-force-border"; var start = builder.pos, end = start + text.length; for (;;) { // Find the part that overlaps with the start of this text var part = (void 0); for (var i = 0; i < order.length; i++) { part = order[i]; if (part.to > start && part.from <= start) { break } } if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); startStyle = null; text = text.slice(part.to - start); start = part.to; } } } function buildCollapsedSpan(builder, size, marker, ignoreWidget) { var widget = !ignoreWidget && marker.widgetNode; if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { if (!widget) { widget = builder.content.appendChild(document.createElement("span")); } widget.setAttribute("cm-marker", marker.id); } if (widget) { builder.cm.display.input.setUneditable(widget); builder.content.appendChild(widget); } builder.pos += size; builder.trailingSpace = false; } // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { var spans = line.markedSpans, allText = line.text, at = 0; if (!spans) { for (var i$1 = 1; i$1 < styles.length; i$1+=2) { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } return } var len = allText.length, pos = 0, i = 1, text = "", style, css; var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; for (;;) { if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = css = ""; attributes = null; collapsed = null; nextChange = Infinity; var foundBookmarks = [], endStyles = (void 0); for (var j = 0; j < spans.length; ++j) { var sp = spans[j], m = sp.marker; if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { foundBookmarks.push(m); } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { if (sp.to != null && sp.to != pos && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; } if (m.className) { spanStyle += " " + m.className; } if (m.css) { css = (css ? css + ";" : "") + m.css; } if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } // support for the old title property // https://github.com/codemirror/CodeMirror/pull/5673 if (m.title) { (attributes || (attributes = {})).title = m.title; } if (m.attributes) { for (var attr in m.attributes) { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } } if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) { collapsed = sp; } } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from; } } if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, collapsed.marker, collapsed.from == null); if (collapsed.to == null) { return } if (collapsed.to == pos) { collapsed = false; } } } if (pos >= len) { break } var upto = Math.min(len, nextChange); while (true) { if (text) { var end = pos + text.length; if (!collapsed) { var tokenText = end > upto ? text.slice(0, upto - pos) : text; builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} pos = end; spanStartStyle = ""; } text = allText.slice(at, at = styles[i++]); style = interpretTokenStyle(styles[i++], builder.cm.options); } } } // These objects are used to represent the visible (currently drawn) // part of the document. A LineView may correspond to multiple // logical lines, if those are connected by collapsed ranges. function LineView(doc, line, lineN) { // The starting line this.line = line; // Continuing lines, if any this.rest = visualLineContinued(line); // Number of logical lines in this visual line this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; this.node = this.text = null; this.hidden = lineIsHidden(doc, line); } // Create a range of LineView objects for the given lines. function buildViewArray(cm, from, to) { var array = [], nextPos; for (var pos = from; pos < to; pos = nextPos) { var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); nextPos = pos + view.size; array.push(view); } return array } var operationGroup = null; function pushOperation(op) { if (operationGroup) { operationGroup.ops.push(op); } else { op.ownsGroup = operationGroup = { ops: [op], delayedCallbacks: [] }; } } function fireCallbacksForOps(group) { // Calls delayed callbacks and cursorActivity handlers until no // new ones appear var callbacks = group.delayedCallbacks, i = 0; do { for (; i < callbacks.length; i++) { callbacks[i].call(null); } for (var j = 0; j < group.ops.length; j++) { var op = group.ops[j]; if (op.cursorActivityHandlers) { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } } } while (i < callbacks.length) } function finishOperation(op, endCb) { var group = op.ownsGroup; if (!group) { return } try { fireCallbacksForOps(group); } finally { operationGroup = null; endCb(group); } } var orphanDelayedCallbacks = null; // Often, we want to signal events at a point where we are in the // middle of some work, but don't want the handler to start calling // other methods on the editor, which might be in an inconsistent // state or simply not expect any other events to happen. // signalLater looks whether there are any handlers, and schedules // them to be executed when the last operation ends, or, if no // operation is active, when a timeout fires. function signalLater(emitter, type /*, values...*/) { var arr = getHandlers(emitter, type); if (!arr.length) { return } var args = Array.prototype.slice.call(arguments, 2), list; if (operationGroup) { list = operationGroup.delayedCallbacks; } else if (orphanDelayedCallbacks) { list = orphanDelayedCallbacks; } else { list = orphanDelayedCallbacks = []; setTimeout(fireOrphanDelayed, 0); } var loop = function ( i ) { list.push(function () { return arr[i].apply(null, args); }); }; for (var i = 0; i < arr.length; ++i) loop( i ); } function fireOrphanDelayed() { var delayed = orphanDelayedCallbacks; orphanDelayedCallbacks = null; for (var i = 0; i < delayed.length; ++i) { delayed[i](); } } // When an aspect of a line changes, a string is added to // lineView.changes. This updates the relevant part of the line's // DOM structure. function updateLineForChanges(cm, lineView, lineN, dims) { for (var j = 0; j < lineView.changes.length; j++) { var type = lineView.changes[j]; if (type == "text") { updateLineText(cm, lineView); } else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } else if (type == "class") { updateLineClasses(cm, lineView); } else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } } lineView.changes = null; } // Lines with gutter elements, widgets or a background class need to // be wrapped, and have the extra elements added to the wrapper div function ensureLineWrapped(lineView) { if (lineView.node == lineView.text) { lineView.node = elt("div", null, null, "position: relative"); if (lineView.text.parentNode) { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } lineView.node.appendChild(lineView.text); if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } } return lineView.node } function updateLineBackground(cm, lineView) { var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; if (cls) { cls += " CodeMirror-linebackground"; } if (lineView.background) { if (cls) { lineView.background.className = cls; } else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } } else if (cls) { var wrap = ensureLineWrapped(lineView); lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); cm.display.input.setUneditable(lineView.background); } } // Wrapper around buildLineContent which will reuse the structure // in display.externalMeasured when possible. function getLineContent(cm, lineView) { var ext = cm.display.externalMeasured; if (ext && ext.line == lineView.line) { cm.display.externalMeasured = null; lineView.measure = ext.measure; return ext.built } return buildLineContent(cm, lineView) } // Redraw the line's text. Interacts with the background and text // classes because the mode may output tokens that influence these // classes. function updateLineText(cm, lineView) { var cls = lineView.text.className; var built = getLineContent(cm, lineView); if (lineView.text == lineView.node) { lineView.node = built.pre; } lineView.text.parentNode.replaceChild(built.pre, lineView.text); lineView.text = built.pre; if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { lineView.bgClass = built.bgClass; lineView.textClass = built.textClass; updateLineClasses(cm, lineView); } else if (cls) { lineView.text.className = cls; } } function updateLineClasses(cm, lineView) { updateLineBackground(cm, lineView); if (lineView.line.wrapClass) { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } else if (lineView.node != lineView.text) { lineView.node.className = ""; } var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; lineView.text.className = textClass || ""; } function updateLineGutter(cm, lineView, lineN, dims) { if (lineView.gutter) { lineView.node.removeChild(lineView.gutter); lineView.gutter = null; } if (lineView.gutterBackground) { lineView.node.removeChild(lineView.gutterBackground); lineView.gutterBackground = null; } if (lineView.line.gutterClass) { var wrap = ensureLineWrapped(lineView); lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); cm.display.input.setUneditable(lineView.gutterBackground); wrap.insertBefore(lineView.gutterBackground, lineView.text); } var markers = lineView.line.gutterMarkers; if (cm.options.lineNumbers || markers) { var wrap$1 = ensureLineWrapped(lineView); var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); cm.display.input.setUneditable(gutterWrap); wrap$1.insertBefore(gutterWrap, lineView.text); if (lineView.line.gutterClass) { gutterWrap.className += " " + lineView.line.gutterClass; } if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) { lineView.lineNumber = gutterWrap.appendChild( elt("div", lineNumberFor(cm.options, lineN), "CodeMirror-linenumber CodeMirror-gutter-elt", ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; if (found) { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } } } } } function updateLineWidgets(cm, lineView, dims) { if (lineView.alignable) { lineView.alignable = null; } for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { next = node.nextSibling; if (node.className == "CodeMirror-linewidget") { lineView.node.removeChild(node); } } insertLineWidgets(cm, lineView, dims); } // Build a line's DOM representation from scratch function buildLineElement(cm, lineView, lineN, dims) { var built = getLineContent(cm, lineView); lineView.text = lineView.node = built.pre; if (built.bgClass) { lineView.bgClass = built.bgClass; } if (built.textClass) { lineView.textClass = built.textClass; } updateLineClasses(cm, lineView); updateLineGutter(cm, lineView, lineN, dims); insertLineWidgets(cm, lineView, dims); return lineView.node } // A lineView may contain multiple logical lines (when merged by // collapsed spans). The widgets for all of them need to be drawn. function insertLineWidgets(cm, lineView, dims) { insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } } function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { if (!line.widgets) { return } var wrap = ensureLineWrapped(lineView); for (var i = 0, ws = line.widgets; i < ws.length; ++i) { var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } positionLineWidget(widget, node, lineView, dims); cm.display.input.setUneditable(node); if (allowAbove && widget.above) { wrap.insertBefore(node, lineView.gutter || lineView.text); } else { wrap.appendChild(node); } signalLater(widget, "redraw"); } } function positionLineWidget(widget, node, lineView, dims) { if (widget.noHScroll) { (lineView.alignable || (lineView.alignable = [])).push(node); var width = dims.wrapperWidth; node.style.left = dims.fixedPos + "px"; if (!widget.coverGutter) { width -= dims.gutterTotalWidth; node.style.paddingLeft = dims.gutterTotalWidth + "px"; } node.style.width = width + "px"; } if (widget.coverGutter) { node.style.zIndex = 5; node.style.position = "relative"; if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } } } function widgetHeight(widget) { if (widget.height != null) { return widget.height } var cm = widget.doc.cm; if (!cm) { return 0 } if (!contains(document.body, widget.node)) { var parentStyle = "position: relative;"; if (widget.coverGutter) { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } if (widget.noHScroll) { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); } return widget.height = widget.node.parentNode.offsetHeight } // Return true when the given mouse event happened in a widget function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || (n.parentNode == display.sizer && n != display.mover)) { return true } } } // POSITION MEASUREMENT function paddingTop(display) {return display.lineSpace.offsetTop} function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} function paddingH(display) { if (display.cachedPaddingH) { return display.cachedPaddingH } var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } return data } function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } function displayWidth(cm) { return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth } function displayHeight(cm) { return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight } // Ensure the lineView.wrapping.heights array is populated. This is // an array of bottom offsets for the lines that make up a drawn // line. When lineWrapping is on, there might be more than one // height. function ensureLineHeights(cm, lineView, rect) { var wrapping = cm.options.lineWrapping; var curWidth = wrapping && displayWidth(cm); if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { var heights = lineView.measure.heights = []; if (wrapping) { lineView.measure.width = curWidth; var rects = lineView.text.firstChild.getClientRects(); for (var i = 0; i < rects.length - 1; i++) { var cur = rects[i], next = rects[i + 1]; if (Math.abs(cur.bottom - next.bottom) > 2) { heights.push((cur.bottom + next.top) / 2 - rect.top); } } } heights.push(rect.bottom - rect.top); } } // Find a line map (mapping character offsets to text nodes) and a // measurement cache for the given line number. (A line view might // contain multiple lines when collapsed ranges are present.) function mapFromLineView(lineView, line, lineN) { if (lineView.line == line) { return {map: lineView.measure.map, cache: lineView.measure.cache} } for (var i = 0; i < lineView.rest.length; i++) { if (lineView.rest[i] == line) { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) { if (lineNo(lineView.rest[i$1]) > lineN) { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } } // Render a line into the hidden node display.externalMeasured. Used // when measurement is needed for a line that's not in the viewport. function updateExternalMeasurement(cm, line) { line = visualLine(line); var lineN = lineNo(line); var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); view.lineN = lineN; var built = view.built = buildLineContent(cm, view); view.text = built.pre; removeChildrenAndAdd(cm.display.lineMeasure, built.pre); return view } // Get a {top, bottom, left, right} box (in line-local coordinates) // for a given character. function measureChar(cm, line, ch, bias) { return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) } // Find a line view that corresponds to the given line number. function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) { return cm.display.view[findViewIndex(cm, lineN)] } var ext = cm.display.externalMeasured; if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) { return ext } } // Measurement can be split in two steps, the set-up work that // applies to the whole line, and the measurement of the actual // character. Functions like coordsChar, that need to do a lot of // measurements in a row, can thus ensure that the set-up work is // only done once. function prepareMeasureForLine(cm, line) { var lineN = lineNo(line); var view = findViewForLine(cm, lineN); if (view && !view.text) { view = null; } else if (view && view.changes) { updateLineForChanges(cm, view, lineN, getDimensions(cm)); cm.curOp.forceUpdate = true; } if (!view) { view = updateExternalMeasurement(cm, line); } var info = mapFromLineView(view, line, lineN); return { line: line, view: view, rect: null, map: info.map, cache: info.cache, before: info.before, hasHeights: false } } // Given a prepared measurement object, measures the position of an // actual character (or fetches it from the cache). function measureCharPrepared(cm, prepared, ch, bias, varHeight) { if (prepared.before) { ch = -1; } var key = ch + (bias || ""), found; if (prepared.cache.hasOwnProperty(key)) { found = prepared.cache[key]; } else { if (!prepared.rect) { prepared.rect = prepared.view.text.getBoundingClientRect(); } if (!prepared.hasHeights) { ensureLineHeights(cm, prepared.view, prepared.rect); prepared.hasHeights = true; } found = measureCharInner(cm, prepared, ch, bias); if (!found.bogus) { prepared.cache[key] = found; } } return {left: found.left, right: found.right, top: varHeight ? found.rtop : found.top, bottom: varHeight ? found.rbottom : found.bottom} } var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; function nodeAndOffsetInLineMap(map$$1, ch, bias) { var node, start, end, collapse, mStart, mEnd; // First, search the line map for the text node corresponding to, // or closest to, the target character. for (var i = 0; i < map$$1.length; i += 3) { mStart = map$$1[i]; mEnd = map$$1[i + 1]; if (ch < mStart) { start = 0; end = 1; collapse = "left"; } else if (ch < mEnd) { start = ch - mStart; end = start + 1; } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) { end = mEnd - mStart; start = end - 1; if (ch >= mEnd) { collapse = "right"; } } if (start != null) { node = map$$1[i + 2]; if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) { collapse = bias; } if (bias == "left" && start == 0) { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) { node = map$$1[(i -= 3) + 2]; collapse = "left"; } } if (bias == "right" && start == mEnd - mStart) { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) { node = map$$1[(i += 3) + 2]; collapse = "right"; } } break } } return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} } function getUsefulRect(rects, bias) { var rect = nullRect; if (bias == "left") { for (var i = 0; i < rects.length; i++) { if ((rect = rects[i]).left != rect.right) { break } } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { if ((rect = rects[i$1]).left != rect.right) { break } } } return rect } function measureCharInner(cm, prepared, ch, bias) { var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); var node = place.node, start = place.start, end = place.end, collapse = place.collapse; var rect; if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) { rect = node.parentNode.getBoundingClientRect(); } else { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } if (rect.left || rect.right || start == 0) { break } end = start; start = start - 1; collapse = "right"; } if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } } else { // If it is a widget, simply get the box for the whole widget. if (start > 0) { collapse = bias = "right"; } var rects; if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) { rect = rects[bias == "right" ? rects.length - 1 : 0]; } else { rect = node.getBoundingClientRect(); } } if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { var rSpan = node.parentNode.getClientRects()[0]; if (rSpan) { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } else { rect = nullRect; } } var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; var mid = (rtop + rbot) / 2; var heights = prepared.view.measure.heights; var i = 0; for (; i < heights.length - 1; i++) { if (mid < heights[i]) { break } } var top = i ? heights[i - 1] : 0, bot = heights[i]; var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, top: top, bottom: bot}; if (!rect.left && !rect.right) { result.bogus = true; } if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } return result } // Work around problem with bounding client rects on ranges being // returned incorrectly when zoomed on IE10 and below. function maybeUpdateRectForZooming(measure, rect) { if (!window.screen || screen.logicalXDPI == null || screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) { return rect } var scaleX = screen.logicalXDPI / screen.deviceXDPI; var scaleY = screen.logicalYDPI / screen.deviceYDPI; return {left: rect.left * scaleX, right: rect.right * scaleX, top: rect.top * scaleY, bottom: rect.bottom * scaleY} } function clearLineMeasurementCacheFor(lineView) { if (lineView.measure) { lineView.measure.cache = {}; lineView.measure.heights = null; if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) { lineView.measure.caches[i] = {}; } } } } function clearLineMeasurementCache(cm) { cm.display.externalMeasure = null; removeChildren(cm.display.lineMeasure); for (var i = 0; i < cm.display.view.length; i++) { clearLineMeasurementCacheFor(cm.display.view[i]); } } function clearCaches(cm) { clearLineMeasurementCache(cm); cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } cm.display.lineNumChars = null; } function pageScrollX() { // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 // which causes page_Offset and bounding client rects to use // different reference viewports and invalidate our calculations. if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } return window.pageXOffset || (document.documentElement || document.body).scrollLeft } function pageScrollY() { if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } return window.pageYOffset || (document.documentElement || document.body).scrollTop } function widgetTopHeight(lineObj) { var height = 0; if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) { height += widgetHeight(lineObj.widgets[i]); } } } return height } // Converts a {top, bottom, left, right} box from line-local // coordinates into another coordinate system. Context may be one of // "line", "div" (display.lineDiv), "local"./null (editor), "window", // or "page". function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { if (!includeWidgets) { var height = widgetTopHeight(lineObj); rect.top += height; rect.bottom += height; } if (context == "line") { return rect } if (!context) { context = "local"; } var yOff = heightAtLine(lineObj); if (context == "local") { yOff += paddingTop(cm.display); } else { yOff -= cm.display.viewOffset; } if (context == "page" || context == "window") { var lOff = cm.display.lineSpace.getBoundingClientRect(); yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); rect.left += xOff; rect.right += xOff; } rect.top += yOff; rect.bottom += yOff; return rect } // Coverts a box from "div" coords to another coordinate system. // Context may be "window", "page", "div", or "local"./null. function fromCoordSystem(cm, coords, context) { if (context == "div") { return coords } var left = coords.left, top = coords.top; // First move into "page" coordinate system if (context == "page") { left -= pageScrollX(); top -= pageScrollY(); } else if (context == "local" || !context) { var localBox = cm.display.sizer.getBoundingClientRect(); left += localBox.left; top += localBox.top; } var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} } function charCoords(cm, pos, context, lineObj, bias) { if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) } // Returns a box for a given cursor position, which may have an // 'other' property containing the position of the secondary cursor // on a bidi boundary. // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` // and after `char - 1` in writing order of `char - 1` // A cursor Pos(line, char, "after") is on the same visual line as `char` // and before `char` in writing order of `char` // Examples (upper-case letters are RTL, lower-case are LTR): // Pos(0, 1, ...) // before after // ab a|b a|b // aB a|B aB| // Ab |Ab A|b // AB B|A B|A // Every position after the last character on a line is considered to stick // to the last character on the line. function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { lineObj = lineObj || getLine(cm.doc, pos.line); if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } function get(ch, right) { var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); if (right) { m.left = m.right; } else { m.right = m.left; } return intoCoordSystem(cm, lineObj, m, context) } var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; if (ch >= lineObj.text.length) { ch = lineObj.text.length; sticky = "before"; } else if (ch <= 0) { ch = 0; sticky = "after"; } if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } function getBidi(ch, partPos, invert) { var part = order[partPos], right = part.level == 1; return get(invert ? ch - 1 : ch, right != invert) } var partPos = getBidiPartAt(order, ch, sticky); var other = bidiOther; var val = getBidi(ch, partPos, sticky == "before"); if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } return val } // Used to cheaply estimate the coordinates for a position. Used for // intermediate scroll updates. function estimateCoords(cm, pos) { var left = 0; pos = clipPos(cm.doc, pos); if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } var lineObj = getLine(cm.doc, pos.line); var top = heightAtLine(lineObj) + paddingTop(cm.display); return {left: left, right: left, top: top, bottom: top + lineObj.height} } // Positions returned by coordsChar contain some extra information. // xRel is the relative x position of the input coordinates compared // to the found position (so xRel > 0 means the coordinates are to // the right of the character position, for example). When outside // is true, that means the coordinates lie outside the line's // vertical range. function PosWithInfo(line, ch, sticky, outside, xRel) { var pos = Pos(line, ch, sticky); pos.xRel = xRel; if (outside) { pos.outside = outside; } return pos } // Compute the character position closest to the given coordinates. // Input must be lineSpace-local ("div" coordinate system). function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineN > last) { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } if (x < 0) { x = 0; } var lineObj = getLine(doc, lineN); for (;;) { var found = coordsCharInner(cm, lineObj, lineN, x, y); var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); if (!collapsed) { return found } var rangeEnd = collapsed.find(1); if (rangeEnd.line == lineN) { return rangeEnd } lineObj = getLine(doc, lineN = rangeEnd.line); } } function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { y -= widgetTopHeight(lineObj); var end = lineObj.text.length; var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); return {begin: begin, end: end} } function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) } // Returns true if the given side of a box is after the given // coordinates, in top-to-bottom, left-to-right order. function boxIsAfter(box, x, y, left) { return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x } function coordsCharInner(cm, lineObj, lineNo$$1, x, y) { // Move y into line-local coordinate space y -= heightAtLine(lineObj); var preparedMeasure = prepareMeasureForLine(cm, lineObj); // When directly calling `measureCharPrepared`, we have to adjust // for the widgets at this line. var widgetHeight$$1 = widgetTopHeight(lineObj); var begin = 0, end = lineObj.text.length, ltr = true; var order = getOrder(lineObj, cm.doc.direction); // If the line isn't plain left-to-right text, first figure out // which bidi section the coordinates fall into. if (order) { var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y); ltr = part.level != 1; // The awkward -1 offsets are needed because findFirst (called // on these below) will treat its first bound as inclusive, // second as exclusive, but we want to actually address the // characters in the part's range begin = ltr ? part.from : part.to - 1; end = ltr ? part.to : part.from - 1; } // A binary search to find the first character whose bounding box // starts after the coordinates. If we run across any whose box wrap // the coordinates, store that. var chAround = null, boxAround = null; var ch = findFirst(function (ch) { var box = measureCharPrepared(cm, preparedMeasure, ch); box.top += widgetHeight$$1; box.bottom += widgetHeight$$1; if (!boxIsAfter(box, x, y, false)) { return false } if (box.top <= y && box.left <= x) { chAround = ch; boxAround = box; } return true }, begin, end); var baseX, sticky, outside = false; // If a box around the coordinates was found, use that if (boxAround) { // Distinguish coordinates nearer to the left or right side of the box var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; ch = chAround + (atStart ? 0 : 1); sticky = atStart ? "after" : "before"; baseX = atLeft ? boxAround.left : boxAround.right; } else { // (Adjust for extended bound, if necessary.) if (!ltr && (ch == end || ch == begin)) { ch++; } // To determine which side to associate with, get the box to the // left of the character and compare it's vertical position to the // coordinates sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ? "after" : "before"; // Now get accurate coordinates for this place, in order to get a // base X position var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure); baseX = coords.left; outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; } ch = skipExtendingChars(lineObj.text, ch, 1); return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX) } function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) { // Bidi parts are sorted left-to-right, and in a non-line-wrapping // situation, we can take this ordering to correspond to the visual // ordering. This finds the first part whose end is after the given // coordinates. var index = findFirst(function (i) { var part = order[i], ltr = part.level != 1; return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"), "line", lineObj, preparedMeasure), x, y, true) }, 0, order.length - 1); var part = order[index]; // If this isn't the first part, the part's start is also after // the coordinates, and the coordinates aren't on the same line as // that start, move one part back. if (index > 0) { var ltr = part.level != 1; var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"), "line", lineObj, preparedMeasure); if (boxIsAfter(start, x, y, true) && start.top > y) { part = order[index - 1]; } } return part } function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { // In a wrapped line, rtl text on wrapping boundaries can do things // that don't correspond to the ordering in our `order` array at // all, so a binary search doesn't work, and we want to return a // part that only spans one line so that the binary search in // coordsCharInner is safe. As such, we first find the extent of the // wrapped line, and then do a flat search in which we discard any // spans that aren't on the line. var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); var begin = ref.begin; var end = ref.end; if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } var part = null, closestDist = null; for (var i = 0; i < order.length; i++) { var p = order[i]; if (p.from >= end || p.to <= begin) { continue } var ltr = p.level != 1; var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; // Weigh against spans ending before this, so that they are only // picked if nothing ends after var dist = endX < x ? x - endX + 1e9 : endX - x; if (!part || closestDist > dist) { part = p; closestDist = dist; } } if (!part) { part = order[order.length - 1]; } // Clip the part to the wrapped line. if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } return part } var measureText; // Compute the default text height. function textHeight(display) { if (display.cachedTextHeight != null) { return display.cachedTextHeight } if (measureText == null) { measureText = elt("pre", null, "CodeMirror-line-like"); // Measure a bunch of lines, for browsers that compute // fractional heights. for (var i = 0; i < 49; ++i) { measureText.appendChild(document.createTextNode("x")); measureText.appendChild(elt("br")); } measureText.appendChild(document.createTextNode("x")); } removeChildrenAndAdd(display.measure, measureText); var height = measureText.offsetHeight / 50; if (height > 3) { display.cachedTextHeight = height; } removeChildren(display.measure); return height || 1 } // Compute the default character width. function charWidth(display) { if (display.cachedCharWidth != null) { return display.cachedCharWidth } var anchor = elt("span", "xxxxxxxxxx"); var pre = elt("pre", [anchor], "CodeMirror-line-like"); removeChildrenAndAdd(display.measure, pre); var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; if (width > 2) { display.cachedCharWidth = width; } return width || 10 } // Do a bulk-read of the DOM positions and sizes needed to draw the // view, so that we don't interleave reading and writing to the DOM. function getDimensions(cm) { var d = cm.display, left = {}, width = {}; var gutterLeft = d.gutters.clientLeft; for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { var id = cm.display.gutterSpecs[i].className; left[id] = n.offsetLeft + n.clientLeft + gutterLeft; width[id] = n.clientWidth; } return {fixedPos: compensateForHScroll(d), gutterTotalWidth: d.gutters.offsetWidth, gutterLeft: left, gutterWidth: width, wrapperWidth: d.wrapper.clientWidth} } // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, // but using getBoundingClientRect to get a sub-pixel-accurate // result. function compensateForHScroll(display) { return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left } // Returns a function that estimates the height of a line, to use as // first approximation until the line becomes visible (and is thus // properly measurable). function estimateHeight(cm) { var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); return function (line) { if (lineIsHidden(cm.doc, line)) { return 0 } var widgetsHeight = 0; if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } } } if (wrapping) { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } else { return widgetsHeight + th } } } function estimateLineHeights(cm) { var doc = cm.doc, est = estimateHeight(cm); doc.iter(function (line) { var estHeight = est(line); if (estHeight != line.height) { updateLineHeight(line, estHeight); } }); } // Given a mouse event, find the corresponding position. If liberal // is false, it checks whether a gutter or scrollbar was clicked, // and returns null if it was. forRect is used by rectangular // selections, and tries to estimate a character position even for // coordinates beyond the right of the text. function posFromMouse(cm, e, liberal, forRect) { var display = cm.display; if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } var x, y, space = display.lineSpace.getBoundingClientRect(); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX - space.left; y = e.clientY - space.top; } catch (e) { return null } var coords = coordsChar(cm, x, y), line; if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); } return coords } // Find the view element corresponding to a given line. Return null // when the line isn't visible. function findViewIndex(cm, n) { if (n >= cm.display.viewTo) { return null } n -= cm.display.viewFrom; if (n < 0) { return null } var view = cm.display.view; for (var i = 0; i < view.length; i++) { n -= view[i].size; if (n < 0) { return i } } } // Updates the display.view data structure for a given change to the // document. From and to are in pre-change coordinates. Lendiff is // the amount of lines added or subtracted by the change. This is // used for changes that span multiple lines, or change the way // lines are divided into visual lines. regLineChange (below) // registers single-line changes. function regChange(cm, from, to, lendiff) { if (from == null) { from = cm.doc.first; } if (to == null) { to = cm.doc.first + cm.doc.size; } if (!lendiff) { lendiff = 0; } var display = cm.display; if (lendiff && to < display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers > from)) { display.updateLineNumbers = from; } cm.curOp.viewChanged = true; if (from >= display.viewTo) { // Change after if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) { resetView(cm); } } else if (to <= display.viewFrom) { // Change before if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { resetView(cm); } else { display.viewFrom += lendiff; display.viewTo += lendiff; } } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap resetView(cm); } else if (from <= display.viewFrom) { // Top overlap var cut = viewCuttingPoint(cm, to, to + lendiff, 1); if (cut) { display.view = display.view.slice(cut.index); display.viewFrom = cut.lineN; display.viewTo += lendiff; } else { resetView(cm); } } else if (to >= display.viewTo) { // Bottom overlap var cut$1 = viewCuttingPoint(cm, from, from, -1); if (cut$1) { display.view = display.view.slice(0, cut$1.index); display.viewTo = cut$1.lineN; } else { resetView(cm); } } else { // Gap in the middle var cutTop = viewCuttingPoint(cm, from, from, -1); var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); if (cutTop && cutBot) { display.view = display.view.slice(0, cutTop.index) .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) .concat(display.view.slice(cutBot.index)); display.viewTo += lendiff; } else { resetView(cm); } } var ext = display.externalMeasured; if (ext) { if (to < ext.lineN) { ext.lineN += lendiff; } else if (from < ext.lineN + ext.size) { display.externalMeasured = null; } } } // Register a change to a single line. Type must be one of "text", // "gutter", "class", "widget" function regLineChange(cm, line, type) { cm.curOp.viewChanged = true; var display = cm.display, ext = cm.display.externalMeasured; if (ext && line >= ext.lineN && line < ext.lineN + ext.size) { display.externalMeasured = null; } if (line < display.viewFrom || line >= display.viewTo) { return } var lineView = display.view[findViewIndex(cm, line)]; if (lineView.node == null) { return } var arr = lineView.changes || (lineView.changes = []); if (indexOf(arr, type) == -1) { arr.push(type); } } // Clear the view. function resetView(cm) { cm.display.viewFrom = cm.display.viewTo = cm.doc.first; cm.display.view = []; cm.display.viewOffset = 0; } function viewCuttingPoint(cm, oldN, newN, dir) { var index = findViewIndex(cm, oldN), diff, view = cm.display.view; if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) { return {index: index, lineN: newN} } var n = cm.display.viewFrom; for (var i = 0; i < index; i++) { n += view[i].size; } if (n != oldN) { if (dir > 0) { if (index == view.length - 1) { return null } diff = (n + view[index].size) - oldN; index++; } else { diff = n - oldN; } oldN += diff; newN += diff; } while (visualLineNo(cm.doc, newN) != newN) { if (index == (dir < 0 ? 0 : view.length - 1)) { return null } newN += dir * view[index - (dir < 0 ? 1 : 0)].size; index += dir; } return {index: index, lineN: newN} } // Force the view to cover a given range, adding empty view element // or clipping off existing ones as needed. function adjustView(cm, from, to) { var display = cm.display, view = display.view; if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { display.view = buildViewArray(cm, from, to); display.viewFrom = from; } else { if (display.viewFrom > from) { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } else if (display.viewFrom < from) { display.view = display.view.slice(findViewIndex(cm, from)); } display.viewFrom = from; if (display.viewTo < to) { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } else if (display.viewTo > to) { display.view = display.view.slice(0, findViewIndex(cm, to)); } } display.viewTo = to; } // Count the number of lines in the view whose DOM representation is // out of date (or nonexistent). function countDirtyView(cm) { var view = cm.display.view, dirty = 0; for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } } return dirty } function updateSelection(cm) { cm.display.input.showSelection(cm.display.input.prepareSelection()); } function prepareSelection(cm, primary) { if ( primary === void 0 ) primary = true; var doc = cm.doc, result = {}; var curFragment = result.cursors = document.createDocumentFragment(); var selFragment = result.selection = document.createDocumentFragment(); for (var i = 0; i < doc.sel.ranges.length; i++) { if (!primary && i == doc.sel.primIndex) { continue } var range$$1 = doc.sel.ranges[i]; if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue } var collapsed = range$$1.empty(); if (collapsed || cm.options.showCursorWhenSelecting) { drawSelectionCursor(cm, range$$1.head, curFragment); } if (!collapsed) { drawSelectionRange(cm, range$$1, selFragment); } } return result } // Draws a cursor for the given range function drawSelectionCursor(cm, head, output) { var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); cursor.style.left = pos.left + "px"; cursor.style.top = pos.top + "px"; cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; if (pos.other) { // Secondary cursor, shown when on a 'jump' in bi-directional text var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); otherCursor.style.display = ""; otherCursor.style.left = pos.other.left + "px"; otherCursor.style.top = pos.other.top + "px"; otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; } } function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } // Draws the given range as a highlighted selection function drawSelectionRange(cm, range$$1, output) { var display = cm.display, doc = cm.doc; var fragment = document.createDocumentFragment(); var padding = paddingH(cm.display), leftSide = padding.left; var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; var docLTR = doc.direction == "ltr"; function add(left, top, width, bottom) { if (top < 0) { top = 0; } top = Math.round(top); bottom = Math.round(bottom); fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); } function drawForLine(line, fromArg, toArg) { var lineObj = getLine(doc, line); var lineLen = lineObj.text.length; var start, end; function coords(ch, bias) { return charCoords(cm, Pos(line, ch), "div", lineObj, bias) } function wrapX(pos, dir, side) { var extent = wrappedLineExtentChar(cm, lineObj, null, pos); var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); return coords(ch, prop)[prop] } var order = getOrder(lineObj, doc.direction); iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { var ltr = dir == "ltr"; var fromPos = coords(from, ltr ? "left" : "right"); var toPos = coords(to - 1, ltr ? "right" : "left"); var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; var first = i == 0, last = !order || i == order.length - 1; if (toPos.top - fromPos.top <= 3) { // Single line var openLeft = (docLTR ? openStart : openEnd) && first; var openRight = (docLTR ? openEnd : openStart) && last; var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; add(left, fromPos.top, right - left, fromPos.bottom); } else { // Multiple lines var topLeft, topRight, botLeft, botRight; if (ltr) { topLeft = docLTR && openStart && first ? leftSide : fromPos.left; topRight = docLTR ? rightSide : wrapX(from, dir, "before"); botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); botRight = docLTR && openEnd && last ? rightSide : toPos.right; } else { topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); topRight = !docLTR && openStart && first ? rightSide : fromPos.right; botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); } add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); } if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } if (cmpCoords(toPos, start) < 0) { start = toPos; } if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } if (cmpCoords(toPos, end) < 0) { end = toPos; } }); return {start: start, end: end} } var sFrom = range$$1.from(), sTo = range$$1.to(); if (sFrom.line == sTo.line) { drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); var singleVLine = visualLine(fromLine) == visualLine(toLine); var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); } else { add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); } } if (leftEnd.bottom < rightStart.top) { add(leftSide, leftEnd.bottom, null, rightStart.top); } } output.appendChild(fragment); } // Cursor-blinking function restartBlink(cm) { if (!cm.state.focused) { return } var display = cm.display; clearInterval(display.blinker); var on = true; display.cursorDiv.style.visibility = ""; if (cm.options.cursorBlinkRate > 0) { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); } else if (cm.options.cursorBlinkRate < 0) { display.cursorDiv.style.visibility = "hidden"; } } function ensureFocus(cm) { if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } } function delayBlurEvent(cm) { cm.state.delayingBlurEvent = true; setTimeout(function () { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; onBlur(cm); } }, 100); } function onFocus(cm, e) { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; } if (cm.options.readOnly == "nocursor") { return } if (!cm.state.focused) { signal(cm, "focus", cm, e); cm.state.focused = true; addClass(cm.display.wrapper, "CodeMirror-focused"); // This test prevents this from firing when a context // menu is closed (since the input reset would kill the // select-all detection hack) if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { cm.display.input.reset(); if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 } cm.display.input.receivedFocus(); } restartBlink(cm); } function onBlur(cm, e) { if (cm.state.delayingBlurEvent) { return } if (cm.state.focused) { signal(cm, "blur", cm, e); cm.state.focused = false; rmClass(cm.display.wrapper, "CodeMirror-focused"); } clearInterval(cm.display.blinker); setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); } // Read the actual heights of the rendered lines, and update their // stored heights to match. function updateHeightsInViewport(cm) { var display = cm.display; var prevBottom = display.lineDiv.offsetTop; for (var i = 0; i < display.view.length; i++) { var cur = display.view[i], wrapping = cm.options.lineWrapping; var height = (void 0), width = 0; if (cur.hidden) { continue } if (ie && ie_version < 8) { var bot = cur.node.offsetTop + cur.node.offsetHeight; height = bot - prevBottom; prevBottom = bot; } else { var box = cur.node.getBoundingClientRect(); height = box.bottom - box.top; // Check that lines don't extend past the right of the current // editor width if (!wrapping && cur.text.firstChild) { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } } var diff = cur.line.height - height; if (diff > .005 || diff < -.005) { updateLineHeight(cur.line, height); updateWidgetHeight(cur.line); if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) { updateWidgetHeight(cur.rest[j]); } } } if (width > cm.display.sizerWidth) { var chWidth = Math.ceil(width / charWidth(cm.display)); if (chWidth > cm.display.maxLineLength) { cm.display.maxLineLength = chWidth; cm.display.maxLine = cur.line; cm.display.maxLineChanged = true; } } } } // Read and store the height of line widgets associated with the // given line. function updateWidgetHeight(line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { var w = line.widgets[i], parent = w.node.parentNode; if (parent) { w.height = parent.offsetHeight; } } } } // Compute the lines that are visible in a given viewport (defaults // the the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. function visibleLines(display, doc, viewport) { var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; top = Math.floor(top - paddingTop(display)); var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); // Ensure is a {from: {line, ch}, to: {line, ch}} object, and // forces those lines into the viewport (if possible). if (viewport && viewport.ensure) { var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; if (ensureFrom < from) { from = ensureFrom; to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); } else if (Math.min(ensureTo, doc.lastLine()) >= to) { from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); to = ensureTo; } } return {from: from, to: Math.max(to, from + 1)} } // SCROLLING THINGS INTO VIEW // If an editor sits on the top or bottom of the window, partially // scrolled out of view, this ensures that the cursor is visible. function maybeScrollWindow(cm, rect) { if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; if (rect.top + box.top < 0) { doScroll = true; } else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } if (doScroll != null && !phantom) { var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); cm.display.lineSpace.appendChild(scrollNode); scrollNode.scrollIntoView(doScroll); cm.display.lineSpace.removeChild(scrollNode); } } // Scroll a given position into view (immediately), verifying that // it actually became visible (as line heights are accurately // measured, the position of something may 'drift' during drawing). function scrollPosIntoView(cm, pos, end, margin) { if (margin == null) { margin = 0; } var rect; if (!cm.options.lineWrapping && pos == end) { // Set pos and end to the cursor positions around the character pos sticks to // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch // If pos == Pos(_, 0, "before"), pos and end are unchanged pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; } for (var limit = 0; limit < 5; limit++) { var changed = false; var coords = cursorCoords(cm, pos); var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); rect = {left: Math.min(coords.left, endCoords.left), top: Math.min(coords.top, endCoords.top) - margin, right: Math.max(coords.left, endCoords.left), bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; var scrollPos = calculateScrollPos(cm, rect); var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } } if (!changed) { break } } return rect } // Scroll a given set of coordinates into view (immediately). function scrollIntoView(cm, rect) { var scrollPos = calculateScrollPos(cm, rect); if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } } // Calculate a new scroll position needed to scroll the given // rectangle into view. Returns an object with scrollTop and // scrollLeft properties. When these are undefined, the // vertical/horizontal position does not need to be adjusted. function calculateScrollPos(cm, rect) { var display = cm.display, snapMargin = textHeight(cm.display); if (rect.top < 0) { rect.top = 0; } var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; var screen = displayHeight(cm), result = {}; if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } var docBottom = cm.doc.height + paddingVert(display); var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; if (rect.top < screentop) { result.scrollTop = atTop ? 0 : rect.top; } else if (rect.bottom > screentop + screen) { var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); if (newTop != screentop) { result.scrollTop = newTop; } } var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); var tooWide = rect.right - rect.left > screenw; if (tooWide) { rect.right = rect.left + screenw; } if (rect.left < 10) { result.scrollLeft = 0; } else if (rect.left < screenleft) { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); } else if (rect.right > screenw + screenleft - 3) { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } return result } // Store a relative adjustment to the scroll position in the current // operation (to be applied when the operation finishes). function addToScrollTop(cm, top) { if (top == null) { return } resolveScrollToPos(cm); cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; } // Make sure that at the end of the operation the current cursor is // shown. function ensureCursorVisible(cm) { resolveScrollToPos(cm); var cur = cm.getCursor(); cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; } function scrollToCoords(cm, x, y) { if (x != null || y != null) { resolveScrollToPos(cm); } if (x != null) { cm.curOp.scrollLeft = x; } if (y != null) { cm.curOp.scrollTop = y; } } function scrollToRange(cm, range$$1) { resolveScrollToPos(cm); cm.curOp.scrollToPos = range$$1; } // When an operation has its scrollToPos property set, and another // scroll action is applied before the end of the operation, this // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. function resolveScrollToPos(cm) { var range$$1 = cm.curOp.scrollToPos; if (range$$1) { cm.curOp.scrollToPos = null; var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to); scrollToCoordsRange(cm, from, to, range$$1.margin); } } function scrollToCoordsRange(cm, from, to, margin) { var sPos = calculateScrollPos(cm, { left: Math.min(from.left, to.left), top: Math.min(from.top, to.top) - margin, right: Math.max(from.right, to.right), bottom: Math.max(from.bottom, to.bottom) + margin }); scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); } // Sync the scrollable area and scrollbars, ensure the viewport // covers the visible area. function updateScrollTop(cm, val) { if (Math.abs(cm.doc.scrollTop - val) < 2) { return } if (!gecko) { updateDisplaySimple(cm, {top: val}); } setScrollTop(cm, val, true); if (gecko) { updateDisplaySimple(cm); } startWorker(cm, 100); } function setScrollTop(cm, val, forceScroll) { val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); if (cm.display.scroller.scrollTop == val && !forceScroll) { return } cm.doc.scrollTop = val; cm.display.scrollbars.setScrollTop(val); if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } } // Sync scroller and scrollbar, ensure the gutter elements are // aligned. function setScrollLeft(cm, val, isScroller, forceScroll) { val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } cm.doc.scrollLeft = val; alignHorizontally(cm); if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } cm.display.scrollbars.setScrollLeft(val); } // SCROLLBARS // Prepare DOM reads needed to update the scrollbars. Done in one // shot to minimize update/measure roundtrips. function measureForScrollbars(cm) { var d = cm.display, gutterW = d.gutters.offsetWidth; var docH = Math.round(cm.doc.height + paddingVert(cm.display)); return { clientHeight: d.scroller.clientHeight, viewHeight: d.wrapper.clientHeight, scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, viewWidth: d.wrapper.clientWidth, barLeft: cm.options.fixedGutter ? gutterW : 0, docHeight: docH, scrollHeight: docH + scrollGap(cm) + d.barHeight, nativeBarWidth: d.nativeBarWidth, gutterWidth: gutterW } } var NativeScrollbars = function(place, scroll, cm) { this.cm = cm; var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); vert.tabIndex = horiz.tabIndex = -1; place(vert); place(horiz); on(vert, "scroll", function () { if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } }); on(horiz, "scroll", function () { if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } }); this.checkedZeroWidth = false; // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } }; NativeScrollbars.prototype.update = function (measure) { var needsH = measure.scrollWidth > measure.clientWidth + 1; var needsV = measure.scrollHeight > measure.clientHeight + 1; var sWidth = measure.nativeBarWidth; if (needsV) { this.vert.style.display = "block"; this.vert.style.bottom = needsH ? sWidth + "px" : "0"; var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); // A bug in IE8 can cause this value to be negative, so guard it. this.vert.firstChild.style.height = Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; } else { this.vert.style.display = ""; this.vert.firstChild.style.height = "0"; } if (needsH) { this.horiz.style.display = "block"; this.horiz.style.right = needsV ? sWidth + "px" : "0"; this.horiz.style.left = measure.barLeft + "px"; var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); this.horiz.firstChild.style.width = Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; } else { this.horiz.style.display = ""; this.horiz.firstChild.style.width = "0"; } if (!this.checkedZeroWidth && measure.clientHeight > 0) { if (sWidth == 0) { this.zeroWidthHack(); } this.checkedZeroWidth = true; } return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} }; NativeScrollbars.prototype.setScrollLeft = function (pos) { if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } }; NativeScrollbars.prototype.setScrollTop = function (pos) { if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } }; NativeScrollbars.prototype.zeroWidthHack = function () { var w = mac && !mac_geMountainLion ? "12px" : "18px"; this.horiz.style.height = this.vert.style.width = w; this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; this.disableHoriz = new Delayed; this.disableVert = new Delayed; }; NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { bar.style.pointerEvents = "auto"; function maybeDisable() { // To find out whether the scrollbar is still visible, we // check whether the element under the pixel in the bottom // right corner of the scrollbar box is the scrollbar box // itself (when the bar is still visible) or its filler child // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. var box = bar.getBoundingClientRect(); var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); if (elt$$1 != bar) { bar.style.pointerEvents = "none"; } else { delay.set(1000, maybeDisable); } } delay.set(1000, maybeDisable); }; NativeScrollbars.prototype.clear = function () { var parent = this.horiz.parentNode; parent.removeChild(this.horiz); parent.removeChild(this.vert); }; var NullScrollbars = function () {}; NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; NullScrollbars.prototype.setScrollLeft = function () {}; NullScrollbars.prototype.setScrollTop = function () {}; NullScrollbars.prototype.clear = function () {}; function updateScrollbars(cm, measure) { if (!measure) { measure = measureForScrollbars(cm); } var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; updateScrollbarsInner(cm, measure); for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { if (startWidth != cm.display.barWidth && cm.options.lineWrapping) { updateHeightsInViewport(cm); } updateScrollbarsInner(cm, measureForScrollbars(cm)); startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; } } // Re-synchronize the fake scrollbars with the actual size of the // content. function updateScrollbarsInner(cm, measure) { var d = cm.display; var sizes = d.scrollbars.update(measure); d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; if (sizes.right && sizes.bottom) { d.scrollbarFiller.style.display = "block"; d.scrollbarFiller.style.height = sizes.bottom + "px"; d.scrollbarFiller.style.width = sizes.right + "px"; } else { d.scrollbarFiller.style.display = ""; } if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { d.gutterFiller.style.display = "block"; d.gutterFiller.style.height = sizes.bottom + "px"; d.gutterFiller.style.width = measure.gutterWidth + "px"; } else { d.gutterFiller.style.display = ""; } } var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; function initScrollbars(cm) { if (cm.display.scrollbars) { cm.display.scrollbars.clear(); if (cm.display.scrollbars.addClass) { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } } cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); // Prevent clicks in the scrollbars from killing focus on(node, "mousedown", function () { if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } }); node.setAttribute("cm-not-content", "true"); }, function (pos, axis) { if (axis == "horizontal") { setScrollLeft(cm, pos); } else { updateScrollTop(cm, pos); } }, cm); if (cm.display.scrollbars.addClass) { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } } // Operations are used to wrap a series of changes to the editor // state in such a way that each change won't have to update the // cursor and display (which would be awkward, slow, and // error-prone). Instead, display updates are batched and then all // combined and executed at once. var nextOpId = 0; // Start a new operation. function startOperation(cm) { cm.curOp = { cm: cm, viewChanged: false, // Flag that indicates that lines might need to be redrawn startHeight: cm.doc.height, // Used to detect need to update scrollbar forceUpdate: false, // Used to force a redraw updateInput: 0, // Whether to reset the input textarea typing: false, // Whether this reset should be careful to leave existing text (for compositing) changeObjs: null, // Accumulated changes, for firing change events cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already selectionChanged: false, // Whether the selection needs to be redrawn updateMaxLine: false, // Set when the widest line needs to be determined anew scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet scrollToPos: null, // Used to scroll to a specific position focus: false, id: ++nextOpId // Unique ID }; pushOperation(cm.curOp); } // Finish an operation, updating the display and signalling delayed events function endOperation(cm) { var op = cm.curOp; if (op) { finishOperation(op, function (group) { for (var i = 0; i < group.ops.length; i++) { group.ops[i].cm.curOp = null; } endOperations(group); }); } } // The DOM updates done when an operation finishes are batched so // that the minimum number of relayouts are required. function endOperations(group) { var ops = group.ops; for (var i = 0; i < ops.length; i++) // Read DOM { endOperation_R1(ops[i]); } for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) { endOperation_W1(ops[i$1]); } for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM { endOperation_R2(ops[i$2]); } for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) { endOperation_W2(ops[i$3]); } for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM { endOperation_finish(ops[i$4]); } } function endOperation_R1(op) { var cm = op.cm, display = cm.display; maybeClipScrollbars(cm); if (op.updateMaxLine) { findMaxLine(cm); } op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || op.scrollToPos.to.line >= display.viewTo) || display.maxLineChanged && cm.options.lineWrapping; op.update = op.mustUpdate && new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); } function endOperation_W1(op) { op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); } function endOperation_R2(op) { var cm = op.cm, display = cm.display; if (op.updatedDisplay) { updateHeightsInViewport(cm); } op.barMeasure = measureForScrollbars(cm); // If the max line changed since it was last measured, measure it, // and ensure the document's width matches it. // updateDisplay_W2 will use these properties to do the actual resizing if (display.maxLineChanged && !cm.options.lineWrapping) { op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; cm.display.sizerWidth = op.adjustWidthTo; op.barMeasure.scrollWidth = Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); } if (op.updatedDisplay || op.selectionChanged) { op.preparedSelection = display.input.prepareSelection(); } } function endOperation_W2(op) { var cm = op.cm; if (op.adjustWidthTo != null) { cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; if (op.maxScrollLeft < cm.doc.scrollLeft) { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } cm.display.maxLineChanged = false; } var takeFocus = op.focus && op.focus == activeElt(); if (op.preparedSelection) { cm.display.input.showSelection(op.preparedSelection, takeFocus); } if (op.updatedDisplay || op.startHeight != cm.doc.height) { updateScrollbars(cm, op.barMeasure); } if (op.updatedDisplay) { setDocumentHeight(cm, op.barMeasure); } if (op.selectionChanged) { restartBlink(cm); } if (cm.state.focused && op.updateInput) { cm.display.input.reset(op.typing); } if (takeFocus) { ensureFocus(op.cm); } } function endOperation_finish(op) { var cm = op.cm, display = cm.display, doc = cm.doc; if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } // Abort mouse wheel delta measurement, when scrolling explicitly if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) { display.wheelStartX = display.wheelStartY = null; } // Propagate the scroll position to the actual DOM scroller if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } // If we need to scroll a specific position into view, do so. if (op.scrollToPos) { var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); maybeScrollWindow(cm, rect); } // Fire events for markers that are hidden/unidden by editing or // undoing var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; if (hidden) { for (var i = 0; i < hidden.length; ++i) { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } if (display.wrapper.offsetHeight) { doc.scrollTop = cm.display.scroller.scrollTop; } // Fire change events, and delayed event handlers if (op.changeObjs) { signal(cm, "changes", cm, op.changeObjs); } if (op.update) { op.update.finish(); } } // Run the given function in an operation function runInOp(cm, f) { if (cm.curOp) { return f() } startOperation(cm); try { return f() } finally { endOperation(cm); } } // Wraps a function in an operation. Returns the wrapped function. function operation(cm, f) { return function() { if (cm.curOp) { return f.apply(cm, arguments) } startOperation(cm); try { return f.apply(cm, arguments) } finally { endOperation(cm); } } } // Used to add methods to editor and doc instances, wrapping them in // operations. function methodOp(f) { return function() { if (this.curOp) { return f.apply(this, arguments) } startOperation(this); try { return f.apply(this, arguments) } finally { endOperation(this); } } } function docMethodOp(f) { return function() { var cm = this.cm; if (!cm || cm.curOp) { return f.apply(this, arguments) } startOperation(cm); try { return f.apply(this, arguments) } finally { endOperation(cm); } } } // HIGHLIGHT WORKER function startWorker(cm, time) { if (cm.doc.highlightFrontier < cm.display.viewTo) { cm.state.highlight.set(time, bind(highlightWorker, cm)); } } function highlightWorker(cm) { var doc = cm.doc; if (doc.highlightFrontier >= cm.display.viewTo) { return } var end = +new Date + cm.options.workTime; var context = getContextBefore(cm, doc.highlightFrontier); var changedLines = []; doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { if (context.line >= cm.display.viewFrom) { // Visible var oldStyles = line.styles; var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; var highlighted = highlightLine(cm, line, context, true); if (resetState) { context.state = resetState; } line.styles = highlighted.styles; var oldCls = line.styleClasses, newCls = highlighted.classes; if (newCls) { line.styleClasses = newCls; } else if (oldCls) { line.styleClasses = null; } var ischange = !oldStyles || oldStyles.length != line.styles.length || oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } if (ischange) { changedLines.push(context.line); } line.stateAfter = context.save(); context.nextLine(); } else { if (line.text.length <= cm.options.maxHighlightLength) { processLine(cm, line.text, context); } line.stateAfter = context.line % 5 == 0 ? context.save() : null; context.nextLine(); } if (+new Date > end) { startWorker(cm, cm.options.workDelay); return true } }); doc.highlightFrontier = context.line; doc.modeFrontier = Math.max(doc.modeFrontier, context.line); if (changedLines.length) { runInOp(cm, function () { for (var i = 0; i < changedLines.length; i++) { regLineChange(cm, changedLines[i], "text"); } }); } } // DISPLAY DRAWING var DisplayUpdate = function(cm, viewport, force) { var display = cm.display; this.viewport = viewport; // Store some values that we'll need later (but don't want to force a relayout for) this.visible = visibleLines(display, cm.doc, viewport); this.editorIsHidden = !display.wrapper.offsetWidth; this.wrapperHeight = display.wrapper.clientHeight; this.wrapperWidth = display.wrapper.clientWidth; this.oldDisplayWidth = displayWidth(cm); this.force = force; this.dims = getDimensions(cm); this.events = []; }; DisplayUpdate.prototype.signal = function (emitter, type) { if (hasHandler(emitter, type)) { this.events.push(arguments); } }; DisplayUpdate.prototype.finish = function () { var this$1 = this; for (var i = 0; i < this.events.length; i++) { signal.apply(null, this$1.events[i]); } }; function maybeClipScrollbars(cm) { var display = cm.display; if (!display.scrollbarsClipped && display.scroller.offsetWidth) { display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; display.heightForcer.style.height = scrollGap(cm) + "px"; display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; display.scrollbarsClipped = true; } } function selectionSnapshot(cm) { if (cm.hasFocus()) { return null } var active = activeElt(); if (!active || !contains(cm.display.lineDiv, active)) { return null } var result = {activeElt: active}; if (window.getSelection) { var sel = window.getSelection(); if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { result.anchorNode = sel.anchorNode; result.anchorOffset = sel.anchorOffset; result.focusNode = sel.focusNode; result.focusOffset = sel.focusOffset; } } return result } function restoreSelection(snapshot) { if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } snapshot.activeElt.focus(); if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { var sel = window.getSelection(), range$$1 = document.createRange(); range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset); range$$1.collapse(false); sel.removeAllRanges(); sel.addRange(range$$1); sel.extend(snapshot.focusNode, snapshot.focusOffset); } } // Does the actual updating of the line display. Bails out // (returning false) when there is nothing to be done and forced is // false. function updateDisplayIfNeeded(cm, update) { var display = cm.display, doc = cm.doc; if (update.editorIsHidden) { resetView(cm); return false } // Bail out if the visible area is already rendered and nothing changed. if (!update.force && update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && display.renderedView == display.view && countDirtyView(cm) == 0) { return false } if (maybeUpdateLineNumberWidth(cm)) { resetView(cm); update.dims = getDimensions(cm); } // Compute a suitable new viewport (from & to) var end = doc.first + doc.size; var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); var to = Math.min(end, update.visible.to + cm.options.viewportMargin); if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } if (sawCollapsedSpans) { from = visualLineNo(cm.doc, from); to = visualLineEndNo(cm.doc, to); } var different = from != display.viewFrom || to != display.viewTo || display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; adjustView(cm, from, to); display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); // Position the mover div to align with the current scroll position cm.display.mover.style.top = display.viewOffset + "px"; var toUpdate = countDirtyView(cm); if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) { return false } // For big changes, we hide the enclosing element during the // update, since that speeds up the operations on most browsers. var selSnapshot = selectionSnapshot(cm); if (toUpdate > 4) { display.lineDiv.style.display = "none"; } patchDisplay(cm, display.updateLineNumbers, update.dims); if (toUpdate > 4) { display.lineDiv.style.display = ""; } display.renderedView = display.view; // There might have been a widget with a focused element that got // hidden or updated, if so re-focus it. restoreSelection(selSnapshot); // Prevent selection and cursors from interfering with the scroll // width and height. removeChildren(display.cursorDiv); removeChildren(display.selectionDiv); display.gutters.style.height = display.sizer.style.minHeight = 0; if (different) { display.lastWrapHeight = update.wrapperHeight; display.lastWrapWidth = update.wrapperWidth; startWorker(cm, 400); } display.updateLineNumbers = null; return true } function postUpdateDisplay(cm, update) { var viewport = update.viewport; for (var first = true;; first = false) { if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { // Clip forced viewport to actual scrollable area. if (viewport && viewport.top != null) { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } // Updated line heights might result in the drawn area not // actually covering the viewport. Keep looping until it does. update.visible = visibleLines(cm.display, cm.doc, viewport); if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) { break } } if (!updateDisplayIfNeeded(cm, update)) { break } updateHeightsInViewport(cm); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.force = false; } update.signal(cm, "update", cm); if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; } } function updateDisplaySimple(cm, viewport) { var update = new DisplayUpdate(cm, viewport); if (updateDisplayIfNeeded(cm, update)) { updateHeightsInViewport(cm); postUpdateDisplay(cm, update); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.finish(); } } // Sync the actual display DOM structure with display.view, removing // nodes for lines that are no longer in view, and creating the ones // that are not there yet, and updating the ones that are out of // date. function patchDisplay(cm, updateNumbersFrom, dims) { var display = cm.display, lineNumbers = cm.options.lineNumbers; var container = display.lineDiv, cur = container.firstChild; function rm(node) { var next = node.nextSibling; // Works around a throw-scroll bug in OS X Webkit if (webkit && mac && cm.display.currentWheelTarget == node) { node.style.display = "none"; } else { node.parentNode.removeChild(node); } return next } var view = display.view, lineN = display.viewFrom; // Loop over the elements in the view, syncing cur (the DOM nodes // in display.lineDiv) with the view as we go. for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet var node = buildLineElement(cm, lineView, lineN, dims); container.insertBefore(node, cur); } else { // Already drawn while (cur != lineView.node) { cur = rm(cur); } var updateNumber = lineNumbers && updateNumbersFrom != null && updateNumbersFrom <= lineN && lineView.lineNumber; if (lineView.changes) { if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } updateLineForChanges(cm, lineView, lineN, dims); } if (updateNumber) { removeChildren(lineView.lineNumber); lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); } cur = lineView.node.nextSibling; } lineN += lineView.size; } while (cur) { cur = rm(cur); } } function updateGutterSpace(display) { var width = display.gutters.offsetWidth; display.sizer.style.marginLeft = width + "px"; } function setDocumentHeight(cm, measure) { cm.display.sizer.style.minHeight = measure.docHeight + "px"; cm.display.heightForcer.style.top = measure.docHeight + "px"; cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; } // Re-align line numbers and gutter marks to compensate for // horizontal scrolling. function alignHorizontally(cm) { var display = cm.display, view = display.view; if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; var gutterW = display.gutters.offsetWidth, left = comp + "px"; for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { if (cm.options.fixedGutter) { if (view[i].gutter) { view[i].gutter.style.left = left; } if (view[i].gutterBackground) { view[i].gutterBackground.style.left = left; } } var align = view[i].alignable; if (align) { for (var j = 0; j < align.length; j++) { align[j].style.left = left; } } } } if (cm.options.fixedGutter) { display.gutters.style.left = (comp + gutterW) + "px"; } } // Used to ensure that the line number gutter is still the right // size for the current document size. Returns true when an update // is needed. function maybeUpdateLineNumberWidth(cm) { if (!cm.options.lineNumbers) { return false } var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; if (last.length != display.lineNumChars) { var test = display.measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")); var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; display.lineGutter.style.width = ""; display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; display.lineNumWidth = display.lineNumInnerWidth + padding; display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; display.lineGutter.style.width = display.lineNumWidth + "px"; updateGutterSpace(cm.display); return true } return false } function getGutters(gutters, lineNumbers) { var result = [], sawLineNumbers = false; for (var i = 0; i < gutters.length; i++) { var name = gutters[i], style = null; if (typeof name != "string") { style = name.style; name = name.className; } if (name == "CodeMirror-linenumbers") { if (!lineNumbers) { continue } else { sawLineNumbers = true; } } result.push({className: name, style: style}); } if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } return result } // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. function renderGutters(display) { var gutters = display.gutters, specs = display.gutterSpecs; removeChildren(gutters); display.lineGutter = null; for (var i = 0; i < specs.length; ++i) { var ref = specs[i]; var className = ref.className; var style = ref.style; var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); if (style) { gElt.style.cssText = style; } if (className == "CodeMirror-linenumbers") { display.lineGutter = gElt; gElt.style.width = (display.lineNumWidth || 1) + "px"; } } gutters.style.display = specs.length ? "" : "none"; updateGutterSpace(display); } function updateGutters(cm) { renderGutters(cm.display); regChange(cm); alignHorizontally(cm); } // The display handles the DOM integration, both for input reading // and content drawing. It holds references to DOM nodes and // display-related state. function Display(place, doc, input, options) { var d = this; this.input = input; // Covers bottom-right square when both scrollbars are present. d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); d.scrollbarFiller.setAttribute("cm-not-content", "true"); // Covers bottom of gutter when coverGutterNextToScrollbar is on // and h scrollbar is present. d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); d.gutterFiller.setAttribute("cm-not-content", "true"); // Will contain the actual code, positioned to cover the viewport. d.lineDiv = eltP("div", null, "CodeMirror-code"); // Elements are added to these to represent selection and cursors. d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); d.cursorDiv = elt("div", null, "CodeMirror-cursors"); // A visibility: hidden element used to find the size of things. d.measure = elt("div", null, "CodeMirror-measure"); // When lines outside of the viewport are measured, they are drawn in this. d.lineMeasure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], null, "position: relative; outline: none"); var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); // Moved around its parent to cover visible view. d.mover = elt("div", [lines], null, "position: relative"); // Set to the height of the document, allowing scrolling. d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); d.sizerWidth = null; // Behavior of elts with overflow: auto and padding is // inconsistent across browsers. This is used to ensure the // scrollable area is big enough. d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); // Will contain the gutters, if any. d.gutters = elt("div", null, "CodeMirror-gutters"); d.lineGutter = null; // Actual scrollable element. d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } if (place) { if (place.appendChild) { place.appendChild(d.wrapper); } else { place(d.wrapper); } } // Current rendered range (may be bigger than the view window). d.viewFrom = d.viewTo = doc.first; d.reportedViewFrom = d.reportedViewTo = doc.first; // Information about the rendered lines. d.view = []; d.renderedView = null; // Holds info about a single rendered line when it was rendered // for measurement, while not in view. d.externalMeasured = null; // Empty space (in pixels) above the view d.viewOffset = 0; d.lastWrapHeight = d.lastWrapWidth = 0; d.updateLineNumbers = null; d.nativeBarWidth = d.barHeight = d.barWidth = 0; d.scrollbarsClipped = false; // Used to only resize the line number gutter when necessary (when // the amount of lines crosses a boundary that makes its width change) d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; // Set to true when a non-horizontal-scrolling line widget is // added. As an optimization, line widget aligning is skipped when // this is false. d.alignWidgets = false; d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. d.maxLine = null; d.maxLineLength = 0; d.maxLineChanged = false; // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; // True when shift is held down. d.shift = false; // Used to track whether anything happened since the context menu // was opened. d.selForContextMenu = null; d.activeTouch = null; d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); renderGutters(d); input.init(d); } // Since the delta values reported on mouse wheel events are // unstandardized between browsers and even browser versions, and // generally horribly unpredictable, this code starts by measuring // the scroll effect that the first few mouse wheel events have, // and, from that, detects the way it can convert deltas to pixel // offsets afterwards. // // The reason we want to know the amount a wheel event will scroll // is that it gives us a chance to update the display before the // actual scrolling happens, reducing flickering. var wheelSamples = 0, wheelPixelsPerUnit = null; // Fill in a browser-detected starting value on browsers where we // know one. These don't have to be accurate -- the result of them // being wrong would just be a slight flicker on the first wheel // scroll (if it is large enough). if (ie) { wheelPixelsPerUnit = -.53; } else if (gecko) { wheelPixelsPerUnit = 15; } else if (chrome) { wheelPixelsPerUnit = -.7; } else if (safari) { wheelPixelsPerUnit = -1/3; } function wheelEventDelta(e) { var dx = e.wheelDeltaX, dy = e.wheelDeltaY; if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } else if (dy == null) { dy = e.wheelDelta; } return {x: dx, y: dy} } function wheelEventPixels(e) { var delta = wheelEventDelta(e); delta.x *= wheelPixelsPerUnit; delta.y *= wheelPixelsPerUnit; return delta } function onScrollWheel(cm, e) { var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; var display = cm.display, scroll = display.scroller; // Quit if there's nothing to scroll here var canScrollX = scroll.scrollWidth > scroll.clientWidth; var canScrollY = scroll.scrollHeight > scroll.clientHeight; if (!(dx && canScrollX || dy && canScrollY)) { return } // Webkit browsers on OS X abort momentum scrolls when the target // of the scroll event is removed from the scrollable element. // This hack (see related code in patchDisplay) makes sure the // element is kept around. if (dy && mac && webkit) { outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { for (var i = 0; i < view.length; i++) { if (view[i].node == cur) { cm.display.currentWheelTarget = cur; break outer } } } } // On some browsers, horizontal scrolling will cause redraws to // happen before the gutter has been realigned, causing it to // wriggle around in a most unseemly way. When we have an // estimated pixels/delta value, we just handle horizontal // scrolling entirely here. It'll be slightly off from native, but // better than glitching out. if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { if (dy && canScrollY) { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); // Only prevent default scrolling if vertical scrolling is // actually possible. Otherwise, it causes vertical scroll // jitter on OSX trackpads when deltaX is small and deltaY // is large (issue #3579) if (!dy || (dy && canScrollY)) { e_preventDefault(e); } display.wheelStartX = null; // Abort measurement, if in progress return } // 'Project' the visible viewport to cover the area that is being // scrolled into view (if we know enough to estimate it). if (dy && wheelPixelsPerUnit != null) { var pixels = dy * wheelPixelsPerUnit; var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) { top = Math.max(0, top + pixels - 50); } else { bot = Math.min(cm.doc.height, bot + pixels + 50); } updateDisplaySimple(cm, {top: top, bottom: bot}); } if (wheelSamples < 20) { if (display.wheelStartX == null) { display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; display.wheelDX = dx; display.wheelDY = dy; setTimeout(function () { if (display.wheelStartX == null) { return } var movedX = scroll.scrollLeft - display.wheelStartX; var movedY = scroll.scrollTop - display.wheelStartY; var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || (movedX && display.wheelDX && movedX / display.wheelDX); display.wheelStartX = display.wheelStartY = null; if (!sample) { return } wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); ++wheelSamples; }, 200); } else { display.wheelDX += dx; display.wheelDY += dy; } } } // Selection objects are immutable. A new one is created every time // the selection changes. A selection is one or more non-overlapping // (and non-touching) ranges, sorted, and an integer that indicates // which one is the primary selection (the one that's scrolled into // view, that getCursor returns, etc). var Selection = function(ranges, primIndex) { this.ranges = ranges; this.primIndex = primIndex; }; Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; Selection.prototype.equals = function (other) { var this$1 = this; if (other == this) { return true } if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } for (var i = 0; i < this.ranges.length; i++) { var here = this$1.ranges[i], there = other.ranges[i]; if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } } return true }; Selection.prototype.deepCopy = function () { var this$1 = this; var out = []; for (var i = 0; i < this.ranges.length; i++) { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); } return new Selection(out, this.primIndex) }; Selection.prototype.somethingSelected = function () { var this$1 = this; for (var i = 0; i < this.ranges.length; i++) { if (!this$1.ranges[i].empty()) { return true } } return false }; Selection.prototype.contains = function (pos, end) { var this$1 = this; if (!end) { end = pos; } for (var i = 0; i < this.ranges.length; i++) { var range = this$1.ranges[i]; if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) { return i } } return -1 }; var Range = function(anchor, head) { this.anchor = anchor; this.head = head; }; Range.prototype.from = function () { return minPos(this.anchor, this.head) }; Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; // Take an unsorted, potentially overlapping set of ranges, and // build a selection out of it. 'Consumes' ranges array (modifying // it). function normalizeSelection(cm, ranges, primIndex) { var mayTouch = cm && cm.options.selectionsMayTouch; var prim = ranges[primIndex]; ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); primIndex = indexOf(ranges, prim); for (var i = 1; i < ranges.length; i++) { var cur = ranges[i], prev = ranges[i - 1]; var diff = cmp(prev.to(), cur.from()); if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; if (i <= primIndex) { --primIndex; } ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); } } return new Selection(ranges, primIndex) } function simpleSelection(anchor, head) { return new Selection([new Range(anchor, head || anchor)], 0) } // Compute the position of the end of a change (its 'to' property // refers to the pre-change end). function changeEnd(change) { if (!change.text) { return change.to } return Pos(change.from.line + change.text.length - 1, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) } // Adjust a position to refer to the post-change position of the // same text, or the end of the change if the change covers it. function adjustForChange(pos, change) { if (cmp(pos, change.from) < 0) { return pos } if (cmp(pos, change.to) <= 0) { return changeEnd(change) } var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } return Pos(line, ch) } function computeSelAfterChange(doc, change) { var out = []; for (var i = 0; i < doc.sel.ranges.length; i++) { var range = doc.sel.ranges[i]; out.push(new Range(adjustForChange(range.anchor, change), adjustForChange(range.head, change))); } return normalizeSelection(doc.cm, out, doc.sel.primIndex) } function offsetPos(pos, old, nw) { if (pos.line == old.line) { return Pos(nw.line, pos.ch - old.ch + nw.ch) } else { return Pos(nw.line + (pos.line - old.line), pos.ch) } } // Used by replaceSelections to allow moving the selection to the // start or around the replaced test. Hint may be "start" or "around". function computeReplacedSel(doc, changes, hint) { var out = []; var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; for (var i = 0; i < changes.length; i++) { var change = changes[i]; var from = offsetPos(change.from, oldPrev, newPrev); var to = offsetPos(changeEnd(change), oldPrev, newPrev); oldPrev = change.to; newPrev = to; if (hint == "around") { var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; out[i] = new Range(inv ? to : from, inv ? from : to); } else { out[i] = new Range(from, from); } } return new Selection(out, doc.sel.primIndex) } // Used to get the editor into a consistent state again when options change. function loadMode(cm) { cm.doc.mode = getMode(cm.options, cm.doc.modeOption); resetModeState(cm); } function resetModeState(cm) { cm.doc.iter(function (line) { if (line.stateAfter) { line.stateAfter = null; } if (line.styles) { line.styles = null; } }); cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; startWorker(cm, 100); cm.state.modeGen++; if (cm.curOp) { regChange(cm); } } // DOCUMENT DATA STRUCTURE // By default, updates that start and end at the beginning of a line // are treated specially, in order to make the association of line // widgets and marker elements with the text behave more intuitive. function isWholeLineUpdate(doc, change) { return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && (!doc.cm || doc.cm.options.wholeLineUpdateBefore) } // Perform a change on the document data structure. function updateDoc(doc, change, markedSpans, estimateHeight$$1) { function spansFor(n) {return markedSpans ? markedSpans[n] : null} function update(line, text, spans) { updateLine(line, text, spans, estimateHeight$$1); signalLater(line, "change", line, change); } function linesFor(start, end) { var result = []; for (var i = start; i < end; ++i) { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); } return result } var from = change.from, to = change.to, text = change.text; var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; // Adjust the line structure if (change.full) { doc.insert(0, linesFor(0, text.length)); doc.remove(text.length, doc.size - text.length); } else if (isWholeLineUpdate(doc, change)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. var added = linesFor(0, text.length - 1); update(lastLine, lastLine.text, lastSpans); if (nlines) { doc.remove(from.line, nlines); } if (added.length) { doc.insert(from.line, added); } } else if (firstLine == lastLine) { if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { var added$1 = linesFor(1, text.length - 1); added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added$1); } } else if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); doc.remove(from.line + 1, nlines); } else { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); var added$2 = linesFor(1, text.length - 1); if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } doc.insert(from.line + 1, added$2); } signalLater(doc, "change", doc, change); } // Call f for all linked documents. function linkedDocs(doc, f, sharedHistOnly) { function propagate(doc, skip, sharedHist) { if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { var rel = doc.linked[i]; if (rel.doc == skip) { continue } var shared = sharedHist && rel.sharedHist; if (sharedHistOnly && !shared) { continue } f(rel.doc, shared); propagate(rel.doc, doc, shared); } } } propagate(doc, null, true); } // Attach a document to an editor. function attachDoc(cm, doc) { if (doc.cm) { throw new Error("This document is already in use.") } cm.doc = doc; doc.cm = cm; estimateLineHeights(cm); loadMode(cm); setDirectionClass(cm); if (!cm.options.lineWrapping) { findMaxLine(cm); } cm.options.mode = doc.modeOption; regChange(cm); } function setDirectionClass(cm) { (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); } function directionChanged(cm) { runInOp(cm, function () { setDirectionClass(cm); regChange(cm); }); } function History(startGen) { // Arrays of change events and selections. Doing something adds an // event to done and clears undo. Undoing moves events from done // to undone, redoing moves them in the other direction. this.done = []; this.undone = []; this.undoDepth = Infinity; // Used to track when changes can be merged into a single undo // event this.lastModTime = this.lastSelTime = 0; this.lastOp = this.lastSelOp = null; this.lastOrigin = this.lastSelOrigin = null; // Used by the isClean() method this.generation = this.maxGeneration = startGen || 1; } // Create a history change event from an updateDoc-style change // object. function historyChangeFromChange(doc, change) { var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); return histChange } // Pop all selection events off the end of a history array. Stop at // a change event. function clearSelectionEvents(array) { while (array.length) { var last = lst(array); if (last.ranges) { array.pop(); } else { break } } } // Find the top change event in the history. Pop off selection // events that are in the way. function lastChangeEvent(hist, force) { if (force) { clearSelectionEvents(hist.done); return lst(hist.done) } else if (hist.done.length && !lst(hist.done).ranges) { return lst(hist.done) } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { hist.done.pop(); return lst(hist.done) } } // Register a change in the history. Merges changes that are within // a single operation, or are close together with an origin that // allows merging (starting with "+") into a single event. function addChangeToHistory(doc, change, selAfter, opId) { var hist = doc.history; hist.undone.length = 0; var time = +new Date, cur; var last; if ((hist.lastOp == opId || hist.lastOrigin == change.origin && change.origin && ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || change.origin.charAt(0) == "*")) && (cur = lastChangeEvent(hist, hist.lastOp == opId))) { // Merge this change into the last event last = lst(cur.changes); if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { // Optimized case for simple insertion -- don't want to add // new changesets for every character typed last.to = changeEnd(change); } else { // Add new sub-event cur.changes.push(historyChangeFromChange(doc, change)); } } else { // Can not be merged, start a new event. var before = lst(hist.done); if (!before || !before.ranges) { pushSelectionToHistory(doc.sel, hist.done); } cur = {changes: [historyChangeFromChange(doc, change)], generation: hist.generation}; hist.done.push(cur); while (hist.done.length > hist.undoDepth) { hist.done.shift(); if (!hist.done[0].ranges) { hist.done.shift(); } } } hist.done.push(selAfter); hist.generation = ++hist.maxGeneration; hist.lastModTime = hist.lastSelTime = time; hist.lastOp = hist.lastSelOp = opId; hist.lastOrigin = hist.lastSelOrigin = change.origin; if (!last) { signal(doc, "historyAdded"); } } function selectionEventCanBeMerged(doc, origin, prev, sel) { var ch = origin.charAt(0); return ch == "*" || ch == "+" && prev.ranges.length == sel.ranges.length && prev.somethingSelected() == sel.somethingSelected() && new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) } // Called whenever the selection changes, sets the new selection as // the pending selection in the history, and pushes the old pending // selection into the 'done' array when it was significantly // different (in number of selected ranges, emptiness, or time). function addSelectionToHistory(doc, sel, opId, options) { var hist = doc.history, origin = options && options.origin; // A new event is started when the previous origin does not match // the current, or the origins don't allow matching. Origins // starting with * are always merged, those starting with + are // merged when similar and close together in time. if (opId == hist.lastSelOp || (origin && hist.lastSelOrigin == origin && (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) { hist.done[hist.done.length - 1] = sel; } else { pushSelectionToHistory(sel, hist.done); } hist.lastSelTime = +new Date; hist.lastSelOrigin = origin; hist.lastSelOp = opId; if (options && options.clearRedo !== false) { clearSelectionEvents(hist.undone); } } function pushSelectionToHistory(sel, dest) { var top = lst(dest); if (!(top && top.ranges && top.equals(sel))) { dest.push(sel); } } // Used to store marked span information in the history. function attachLocalSpans(doc, change, from, to) { var existing = change["spans_" + doc.id], n = 0; doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { if (line.markedSpans) { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } ++n; }); } // When un/re-doing restores text containing marked spans, those // that have been explicitly cleared should not be restored. function removeClearedSpans(spans) { if (!spans) { return null } var out; for (var i = 0; i < spans.length; ++i) { if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } else if (out) { out.push(spans[i]); } } return !out ? spans : out.length ? out : null } // Retrieve and filter the old marked spans stored in a change event. function getOldSpans(doc, change) { var found = change["spans_" + doc.id]; if (!found) { return null } var nw = []; for (var i = 0; i < change.text.length; ++i) { nw.push(removeClearedSpans(found[i])); } return nw } // Used for un/re-doing changes from the history. Combines the // result of computing the existing spans with the set of spans that // existed in the history (so that deleting around a span and then // undoing brings back the span). function mergeOldSpans(doc, change) { var old = getOldSpans(doc, change); var stretched = stretchSpansOverChange(doc, change); if (!old) { return stretched } if (!stretched) { return old } for (var i = 0; i < old.length; ++i) { var oldCur = old[i], stretchCur = stretched[i]; if (oldCur && stretchCur) { spans: for (var j = 0; j < stretchCur.length; ++j) { var span = stretchCur[j]; for (var k = 0; k < oldCur.length; ++k) { if (oldCur[k].marker == span.marker) { continue spans } } oldCur.push(span); } } else if (stretchCur) { old[i] = stretchCur; } } return old } // Used both to provide a JSON-safe object in .getHistory, and, when // detaching a document, to split the history in two function copyHistoryArray(events, newGroup, instantiateSel) { var copy = []; for (var i = 0; i < events.length; ++i) { var event = events[i]; if (event.ranges) { copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); continue } var changes = event.changes, newChanges = []; copy.push({changes: newChanges}); for (var j = 0; j < changes.length; ++j) { var change = changes[j], m = (void 0); newChanges.push({from: change.from, to: change.to, text: change.text}); if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { if (indexOf(newGroup, Number(m[1])) > -1) { lst(newChanges)[prop] = change[prop]; delete change[prop]; } } } } } } return copy } // The 'scroll' parameter given to many of these indicated whether // the new cursor position should be scrolled into view after // modifying the selection. // If shift is held or the extend flag is set, extends a range to // include a given position (and optionally a second position). // Otherwise, simply returns the range between the given positions. // Used for cursor motion and such. function extendRange(range, head, other, extend) { if (extend) { var anchor = range.anchor; if (other) { var posBefore = cmp(head, anchor) < 0; if (posBefore != (cmp(other, anchor) < 0)) { anchor = head; head = other; } else if (posBefore != (cmp(head, other) < 0)) { head = other; } } return new Range(anchor, head) } else { return new Range(other || head, head) } } // Extend the primary selection range, discard the rest. function extendSelection(doc, head, other, options, extend) { if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); } // Extend all selections (pos is an array of selections with length // equal the number of selections) function extendSelections(doc, heads, options) { var out = []; var extend = doc.cm && (doc.cm.display.shift || doc.extend); for (var i = 0; i < doc.sel.ranges.length; i++) { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); setSelection(doc, newSel, options); } // Updates a single range in the selection. function replaceOneSelection(doc, i, range, options) { var ranges = doc.sel.ranges.slice(0); ranges[i] = range; setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); } // Reset the selection to a single range. function setSimpleSelection(doc, anchor, head, options) { setSelection(doc, simpleSelection(anchor, head), options); } // Give beforeSelectionChange handlers a change to influence a // selection update. function filterSelectionChange(doc, sel, options) { var obj = { ranges: sel.ranges, update: function(ranges) { var this$1 = this; this.ranges = []; for (var i = 0; i < ranges.length; i++) { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)); } }, origin: options && options.origin }; signal(doc, "beforeSelectionChange", doc, obj); if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } else { return sel } } function setSelectionReplaceHistory(doc, sel, options) { var done = doc.history.done, last = lst(done); if (last && last.ranges) { done[done.length - 1] = sel; setSelectionNoUndo(doc, sel, options); } else { setSelection(doc, sel, options); } } // Set a new selection. function setSelection(doc, sel, options) { setSelectionNoUndo(doc, sel, options); addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); } function setSelectionNoUndo(doc, sel, options) { if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) { sel = filterSelectionChange(doc, sel, options); } var bias = options && options.bias || (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); if (!(options && options.scroll === false) && doc.cm) { ensureCursorVisible(doc.cm); } } function setSelectionInner(doc, sel) { if (sel.equals(doc.sel)) { return } doc.sel = sel; if (doc.cm) { doc.cm.curOp.updateInput = 1; doc.cm.curOp.selectionChanged = true; signalCursorActivity(doc.cm); } signalLater(doc, "cursorActivity", doc); } // Verify that the selection does not partially select any atomic // marked ranges. function reCheckSelection(doc) { setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); } // Return a selection that does not partially select any atomic // ranges. function skipAtomicInSelection(doc, sel, bias, mayClear) { var out; for (var i = 0; i < sel.ranges.length; i++) { var range = sel.ranges[i]; var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); if (out || newAnchor != range.anchor || newHead != range.head) { if (!out) { out = sel.ranges.slice(0, i); } out[i] = new Range(newAnchor, newHead); } } return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel } function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { var line = getLine(doc, pos.line); if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var sp = line.markedSpans[i], m = sp.marker; // Determine if we should prevent the cursor being placed to the left/right of an atomic marker // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it // is with selectLeft/Right var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { signal(m, "beforeCursorEnter"); if (m.explicitlyCleared) { if (!line.markedSpans) { break } else {--i; continue} } } if (!m.atomic) { continue } if (oldPos) { var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); if (dir < 0 ? preventCursorRight : preventCursorLeft) { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) { return skipAtomicInner(doc, near, pos, dir, mayClear) } } var far = m.find(dir < 0 ? -1 : 1); if (dir < 0 ? preventCursorLeft : preventCursorRight) { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null } } } return pos } // Ensure a given position is not inside an atomic range. function skipAtomic(doc, pos, oldPos, bias, mayClear) { var dir = bias || 1; var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); if (!found) { doc.cantEdit = true; return Pos(doc.first, 0) } return found } function movePos(doc, pos, dir, line) { if (dir < 0 && pos.ch == 0) { if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } else { return null } } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } else { return null } } else { return new Pos(pos.line, pos.ch + dir) } } function selectAll(cm) { cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); } // UPDATING // Allow "beforeChange" event handlers to influence a change function filterChange(doc, change, update) { var obj = { canceled: false, from: change.from, to: change.to, text: change.text, origin: change.origin, cancel: function () { return obj.canceled = true; } }; if (update) { obj.update = function (from, to, text, origin) { if (from) { obj.from = clipPos(doc, from); } if (to) { obj.to = clipPos(doc, to); } if (text) { obj.text = text; } if (origin !== undefined) { obj.origin = origin; } }; } signal(doc, "beforeChange", doc, obj); if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } if (obj.canceled) { if (doc.cm) { doc.cm.curOp.updateInput = 2; } return null } return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} } // Apply a change to a document, and add it to the document's // history, and propagating it to all linked documents. function makeChange(doc, change, ignoreReadOnly) { if (doc.cm) { if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } if (doc.cm.state.suppressEdits) { return } } if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { change = filterChange(doc, change, true); if (!change) { return } } // Possibly split or suppress the update based on the presence // of read-only spans in its range. var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); if (split) { for (var i = split.length - 1; i >= 0; --i) { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } } else { makeChangeInner(doc, change); } } function makeChangeInner(doc, change) { if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } var selAfter = computeSelAfterChange(doc, change); addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); var rebased = []; linkedDocs(doc, function (doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); }); } // Revert a change stored in a document's history. function makeChangeFromHistory(doc, type, allowSelectionOnly) { var suppress = doc.cm && doc.cm.state.suppressEdits; if (suppress && !allowSelectionOnly) { return } var hist = doc.history, event, selAfter = doc.sel; var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; // Verify that there is a useable event (so that ctrl-z won't // needlessly clear selection events) var i = 0; for (; i < source.length; i++) { event = source[i]; if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) { break } } if (i == source.length) { return } hist.lastOrigin = hist.lastSelOrigin = null; for (;;) { event = source.pop(); if (event.ranges) { pushSelectionToHistory(event, dest); if (allowSelectionOnly && !event.equals(doc.sel)) { setSelection(doc, event, {clearRedo: false}); return } selAfter = event; } else if (suppress) { source.push(event); return } else { break } } // Build up a reverse change object to add to the opposite history // stack (redo when undoing, and vice versa). var antiChanges = []; pushSelectionToHistory(selAfter, dest); dest.push({changes: antiChanges, generation: hist.generation}); hist.generation = event.generation || ++hist.maxGeneration; var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); var loop = function ( i ) { var change = event.changes[i]; change.origin = type; if (filter && !filterChange(doc, change, false)) { source.length = 0; return {} } antiChanges.push(historyChangeFromChange(doc, change)); var after = i ? computeSelAfterChange(doc, change) : lst(source); makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } var rebased = []; // Propagate to the linked documents linkedDocs(doc, function (doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); }); }; for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { var returned = loop( i$1 ); if ( returned ) return returned.v; } } // Sub-views need their line numbers shifted when text is added // above or below them in the parent document. function shiftDoc(doc, distance) { if (distance == 0) { return } doc.first += distance; doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( Pos(range.anchor.line + distance, range.anchor.ch), Pos(range.head.line + distance, range.head.ch) ); }), doc.sel.primIndex); if (doc.cm) { regChange(doc.cm, doc.first, doc.first - distance, distance); for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) { regLineChange(doc.cm, l, "gutter"); } } } // More lower-level change function, handling only a single document // (not linked ones). function makeChangeSingleDoc(doc, change, selAfter, spans) { if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } if (change.to.line < doc.first) { shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); return } if (change.from.line > doc.lastLine()) { return } // Clip the change to the size of this doc if (change.from.line < doc.first) { var shift = change.text.length - 1 - (doc.first - change.from.line); shiftDoc(doc, shift); change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), text: [lst(change.text)], origin: change.origin}; } var last = doc.lastLine(); if (change.to.line > last) { change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), text: [change.text[0]], origin: change.origin}; } change.removed = getBetween(doc, change.from, change.to); if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } else { updateDoc(doc, change, spans); } setSelectionNoUndo(doc, selAfter, sel_dontScroll); if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) { doc.cantEdit = false; } } // Handle the interaction of a change to a document with the editor // that this document is part of. function makeChangeSingleDocInEditor(cm, change, spans) { var doc = cm.doc, display = cm.display, from = change.from, to = change.to; var recomputeMaxLength = false, checkWidthStart = from.line; if (!cm.options.lineWrapping) { checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); doc.iter(checkWidthStart, to.line + 1, function (line) { if (line == display.maxLine) { recomputeMaxLength = true; return true } }); } if (doc.sel.contains(change.from, change.to) > -1) { signalCursorActivity(cm); } updateDoc(doc, change, spans, estimateHeight(cm)); if (!cm.options.lineWrapping) { doc.iter(checkWidthStart, from.line + change.text.length, function (line) { var len = lineLength(line); if (len > display.maxLineLength) { display.maxLine = line; display.maxLineLength = len; display.maxLineChanged = true; recomputeMaxLength = false; } }); if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } } retreatFrontier(doc, from.line); startWorker(cm, 400); var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display if (change.full) { regChange(cm); } else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) { regLineChange(cm, from.line, "text"); } else { regChange(cm, from.line, to.line + 1, lendiff); } var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); if (changeHandler || changesHandler) { var obj = { from: from, to: to, text: change.text, removed: change.removed, origin: change.origin }; if (changeHandler) { signalLater(cm, "change", cm, obj); } if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } } cm.display.selForContextMenu = null; } function replaceRange(doc, code, from, to, origin) { var assign; if (!to) { to = from; } if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } if (typeof code == "string") { code = doc.splitLines(code); } makeChange(doc, {from: from, to: to, text: code, origin: origin}); } // Rebasing/resetting history to deal with externally-sourced changes function rebaseHistSelSingle(pos, from, to, diff) { if (to < pos.line) { pos.line += diff; } else if (from < pos.line) { pos.line = from; pos.ch = 0; } } // Tries to rebase an array of history events given a change in the // document. If the change touches the same lines as the event, the // event, and everything 'behind' it, is discarded. If the change is // before the event, the event's positions are updated. Uses a // copy-on-write scheme for the positions, to avoid having to // reallocate them all on every rebase, but also avoid problems with // shared position objects being unsafely updated. function rebaseHistArray(array, from, to, diff) { for (var i = 0; i < array.length; ++i) { var sub = array[i], ok = true; if (sub.ranges) { if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } for (var j = 0; j < sub.ranges.length; j++) { rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); } continue } for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { var cur = sub.changes[j$1]; if (to < cur.from.line) { cur.from = Pos(cur.from.line + diff, cur.from.ch); cur.to = Pos(cur.to.line + diff, cur.to.ch); } else if (from <= cur.to.line) { ok = false; break } } if (!ok) { array.splice(0, i + 1); i = 0; } } } function rebaseHist(hist, change) { var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; rebaseHistArray(hist.done, from, to, diff); rebaseHistArray(hist.undone, from, to, diff); } // Utility for applying a change to a line by handle or number, // returning the number and optionally registering the line as // changed. function changeLine(doc, handle, changeType, op) { var no = handle, line = handle; if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } else { no = lineNo(handle); } if (no == null) { return null } if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } return line } // The document is represented as a BTree consisting of leaves, with // chunk of lines in them, and branches, with up to ten leaves or // other branch nodes below them. The top node is always a branch // node, and is the document object itself (meaning it has // additional methods and properties). // // All nodes have parent links. The tree is used both to go from // line numbers to line objects, and to go from objects to numbers. // It also indexes by height, and is used to convert between height // and line object, and to find the total height of the document. // // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html function LeafChunk(lines) { var this$1 = this; this.lines = lines; this.parent = null; var height = 0; for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; height += lines[i].height; } this.height = height; } LeafChunk.prototype = { chunkSize: function() { return this.lines.length }, // Remove the n lines at offset 'at'. removeInner: function(at, n) { var this$1 = this; for (var i = at, e = at + n; i < e; ++i) { var line = this$1.lines[i]; this$1.height -= line.height; cleanUpLine(line); signalLater(line, "delete"); } this.lines.splice(at, n); }, // Helper used to collapse a small branch into a single leaf. collapse: function(lines) { lines.push.apply(lines, this.lines); }, // Insert the given array of lines at offset 'at', count them as // having the given height. insertInner: function(at, lines, height) { var this$1 = this; this.height += height; this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; } }, // Used to iterate over a part of the tree. iterN: function(at, n, op) { var this$1 = this; for (var e = at + n; at < e; ++at) { if (op(this$1.lines[at])) { return true } } } }; function BranchChunk(children) { var this$1 = this; this.children = children; var size = 0, height = 0; for (var i = 0; i < children.length; ++i) { var ch = children[i]; size += ch.chunkSize(); height += ch.height; ch.parent = this$1; } this.size = size; this.height = height; this.parent = null; } BranchChunk.prototype = { chunkSize: function() { return this.size }, removeInner: function(at, n) { var this$1 = this; this.size -= n; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at < sz) { var rm = Math.min(n, sz - at), oldHeight = child.height; child.removeInner(at, rm); this$1.height -= oldHeight - child.height; if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; } if ((n -= rm) == 0) { break } at = 0; } else { at -= sz; } } // If the result is smaller than 25 lines, ensure that it is a // single leaf node. if (this.size - n < 25 && (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { var lines = []; this.collapse(lines); this.children = [new LeafChunk(lines)]; this.children[0].parent = this; } }, collapse: function(lines) { var this$1 = this; for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); } }, insertInner: function(at, lines, height) { var this$1 = this; this.size += lines.length; this.height += height; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at <= sz) { child.insertInner(at, lines, height); if (child.lines && child.lines.length > 50) { // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. var remaining = child.lines.length % 25 + 25; for (var pos = remaining; pos < child.lines.length;) { var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); child.height -= leaf.height; this$1.children.splice(++i, 0, leaf); leaf.parent = this$1; } child.lines = child.lines.slice(0, remaining); this$1.maybeSpill(); } break } at -= sz; } }, // When a node has grown, check whether it should be split. maybeSpill: function() { if (this.children.length <= 10) { return } var me = this; do { var spilled = me.children.splice(me.children.length - 5, 5); var sibling = new BranchChunk(spilled); if (!me.parent) { // Become the parent node var copy = new BranchChunk(me.children); copy.parent = me; me.children = [copy, sibling]; me = copy; } else { me.size -= sibling.size; me.height -= sibling.height; var myIndex = indexOf(me.parent.children, me); me.parent.children.splice(myIndex + 1, 0, sibling); } sibling.parent = me.parent; } while (me.children.length > 10) me.parent.maybeSpill(); }, iterN: function(at, n, op) { var this$1 = this; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at < sz) { var used = Math.min(n, sz - at); if (child.iterN(at, used, op)) { return true } if ((n -= used) == 0) { break } at = 0; } else { at -= sz; } } } }; // Line widgets are block elements displayed above or below a line. var LineWidget = function(doc, node, options) { var this$1 = this; if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) { this$1[opt] = options[opt]; } } } this.doc = doc; this.node = node; }; LineWidget.prototype.clear = function () { var this$1 = this; var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); if (no == null || !ws) { return } for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } } if (!ws.length) { line.widgets = null; } var height = widgetHeight(this); updateLineHeight(line, Math.max(0, line.height - height)); if (cm) { runInOp(cm, function () { adjustScrollWhenAboveVisible(cm, line, -height); regLineChange(cm, no, "widget"); }); signalLater(cm, "lineWidgetCleared", cm, this, no); } }; LineWidget.prototype.changed = function () { var this$1 = this; var oldH = this.height, cm = this.doc.cm, line = this.line; this.height = null; var diff = widgetHeight(this) - oldH; if (!diff) { return } if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } if (cm) { runInOp(cm, function () { cm.curOp.forceUpdate = true; adjustScrollWhenAboveVisible(cm, line, diff); signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); }); } }; eventMixin(LineWidget); function adjustScrollWhenAboveVisible(cm, line, diff) { if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) { addToScrollTop(cm, diff); } } function addLineWidget(doc, handle, node, options) { var widget = new LineWidget(doc, node, options); var cm = doc.cm; if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } changeLine(doc, handle, "widget", function (line) { var widgets = line.widgets || (line.widgets = []); if (widget.insertAt == null) { widgets.push(widget); } else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); } widget.line = line; if (cm && !lineIsHidden(doc, line)) { var aboveVisible = heightAtLine(line) < doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) { addToScrollTop(cm, widget.height); } cm.curOp.forceUpdate = true; } return true }); if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } return widget } // TEXTMARKERS // Created with markText and setBookmark methods. A TextMarker is a // handle that can be used to clear or find a marked position in the // document. Line objects hold arrays (markedSpans) containing // {from, to, marker} object pointing to such marker objects, and // indicating that such a marker is present on that line. Multiple // lines may point to the same marker when it spans across lines. // The spans will have null for their from/to properties when the // marker continues beyond the start/end of the line. Markers have // links back to the lines they currently touch. // Collapsed markers have unique ids, in order to be able to order // them, which is needed for uniquely determining an outer marker // when they overlap (they may nest, but not partially overlap). var nextMarkerId = 0; var TextMarker = function(doc, type) { this.lines = []; this.type = type; this.doc = doc; this.id = ++nextMarkerId; }; // Clear the marker. TextMarker.prototype.clear = function () { var this$1 = this; if (this.explicitlyCleared) { return } var cm = this.doc.cm, withOp = cm && !cm.curOp; if (withOp) { startOperation(cm); } if (hasHandler(this, "clear")) { var found = this.find(); if (found) { signalLater(this, "clear", found.from, found.to); } } var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this$1.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this$1); if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); } else if (cm) { if (span.to != null) { max = lineNo(line); } if (span.from != null) { min = lineNo(line); } } line.markedSpans = removeMarkedSpan(line.markedSpans, span); if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm) { updateLineHeight(line, textHeight(cm.display)); } } if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual); if (len > cm.display.maxLineLength) { cm.display.maxLine = visual; cm.display.maxLineLength = len; cm.display.maxLineChanged = true; } } } if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } this.lines.length = 0; this.explicitlyCleared = true; if (this.atomic && this.doc.cantEdit) { this.doc.cantEdit = false; if (cm) { reCheckSelection(cm.doc); } } if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } if (withOp) { endOperation(cm); } if (this.parent) { this.parent.clear(); } }; // Find the position of the marker in the document. Returns a {from, // to} object by default. Side can be passed to get a specific side // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the // Pos objects returned contain a line object, rather than a line // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function (side, lineObj) { var this$1 = this; if (side == null && this.type == "bookmark") { side = 1; } var from, to; for (var i = 0; i < this.lines.length; ++i) { var line = this$1.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this$1); if (span.from != null) { from = Pos(lineObj ? line : lineNo(line), span.from); if (side == -1) { return from } } if (span.to != null) { to = Pos(lineObj ? line : lineNo(line), span.to); if (side == 1) { return to } } } return from && {from: from, to: to} }; // Signals that the marker's widget changed, and surrounding layout // should be recomputed. TextMarker.prototype.changed = function () { var this$1 = this; var pos = this.find(-1, true), widget = this, cm = this.doc.cm; if (!pos || !cm) { return } runInOp(cm, function () { var line = pos.line, lineN = lineNo(pos.line); var view = findViewForLine(cm, lineN); if (view) { clearLineMeasurementCacheFor(view); cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; } cm.curOp.updateMaxLine = true; if (!lineIsHidden(widget.doc, line) && widget.height != null) { var oldHeight = widget.height; widget.height = null; var dHeight = widgetHeight(widget) - oldHeight; if (dHeight) { updateLineHeight(line, line.height + dHeight); } } signalLater(cm, "markerChanged", cm, this$1); }); }; TextMarker.prototype.attachLine = function (line) { if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp; if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } } this.lines.push(line); }; TextMarker.prototype.detachLine = function (line) { this.lines.splice(indexOf(this.lines, line), 1); if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); } }; eventMixin(TextMarker); // Create a marker, wire it up to the right lines, and function markText(doc, from, to, options, type) { // Shared markers (across linked documents) are handled separately // (markTextShared will call out to this again, once per // document). if (options && options.shared) { return markTextShared(doc, from, to, options, type) } // Ensure we are in an operation. if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } var marker = new TextMarker(doc, type), diff = cmp(from, to); if (options) { copyObj(options, marker, false); } // Don't connect empty markers unless clearWhenEmpty is false if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) { return marker } if (marker.replacedWith) { // Showing up as a widget implies collapsed (widget replaces text) marker.collapsed = true; marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } if (options.insertLeft) { marker.widgetNode.insertLeft = true; } } if (marker.collapsed) { if (conflictingCollapsedRange(doc, from.line, from, to, marker) || from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) { throw new Error("Inserting collapsed marker partially overlapping an existing one") } seeCollapsedSpans(); } if (marker.addToHistory) { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } var curLine = from.line, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function (line) { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) { updateMaxLine = true; } if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } addMarkedSpan(line, new MarkedSpan(marker, curLine == from.line ? from.ch : null, curLine == to.line ? to.ch : null)); ++curLine; }); // lineIsHidden depends on the presence of the spans, so needs a second pass if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } }); } if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } if (marker.readOnly) { seeReadOnlySpans(); if (doc.history.done.length || doc.history.undone.length) { doc.clearHistory(); } } if (marker.collapsed) { marker.id = ++nextMarkerId; marker.atomic = true; } if (cm) { // Sync editor state if (updateMaxLine) { cm.curOp.updateMaxLine = true; } if (marker.collapsed) { regChange(cm, from.line, to.line + 1); } else if (marker.className || marker.startStyle || marker.endStyle || marker.css || marker.attributes || marker.title) { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } if (marker.atomic) { reCheckSelection(cm.doc); } signalLater(cm, "markerAdded", cm, marker); } return marker } // SHARED TEXTMARKERS // A shared marker spans multiple linked documents. It is // implemented as a meta-marker-object controlling multiple normal // markers. var SharedTextMarker = function(markers, primary) { var this$1 = this; this.markers = markers; this.primary = primary; for (var i = 0; i < markers.length; ++i) { markers[i].parent = this$1; } }; SharedTextMarker.prototype.clear = function () { var this$1 = this; if (this.explicitlyCleared) { return } this.explicitlyCleared = true; for (var i = 0; i < this.markers.length; ++i) { this$1.markers[i].clear(); } signalLater(this, "clear"); }; SharedTextMarker.prototype.find = function (side, lineObj) { return this.primary.find(side, lineObj) }; eventMixin(SharedTextMarker); function markTextShared(doc, from, to, options, type) { options = copyObj(options); options.shared = false; var markers = [markText(doc, from, to, options, type)], primary = markers[0]; var widget = options.widgetNode; linkedDocs(doc, function (doc) { if (widget) { options.widgetNode = widget.cloneNode(true); } markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); for (var i = 0; i < doc.linked.length; ++i) { if (doc.linked[i].isParent) { return } } primary = lst(markers); }); return new SharedTextMarker(markers, primary) } function findSharedMarkers(doc) { return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) } function copySharedMarkers(doc, markers) { for (var i = 0; i < markers.length; i++) { var marker = markers[i], pos = marker.find(); var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); if (cmp(mFrom, mTo)) { var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); marker.markers.push(subMark); subMark.parent = marker; } } } function detachSharedMarkers(markers) { var loop = function ( i ) { var marker = markers[i], linked = [marker.primary.doc]; linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); for (var j = 0; j < marker.markers.length; j++) { var subMarker = marker.markers[j]; if (indexOf(linked, subMarker.doc) == -1) { subMarker.parent = null; marker.markers.splice(j--, 1); } } }; for (var i = 0; i < markers.length; i++) loop( i ); } var nextDocId = 0; var Doc = function(text, mode, firstLine, lineSep, direction) { if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } if (firstLine == null) { firstLine = 0; } BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; this.cantEdit = false; this.cleanGeneration = 1; this.modeFrontier = this.highlightFrontier = firstLine; var start = Pos(firstLine, 0); this.sel = simpleSelection(start); this.history = new History(null); this.id = ++nextDocId; this.modeOption = mode; this.lineSep = lineSep; this.direction = (direction == "rtl") ? "rtl" : "ltr"; this.extend = false; if (typeof text == "string") { text = this.splitLines(text); } updateDoc(this, {from: start, to: start, text: text}); setSelection(this, simpleSelection(start), sel_dontScroll); }; Doc.prototype = createObj(BranchChunk.prototype, { constructor: Doc, // Iterate over the document. Supports two forms -- with only one // argument, it calls that for each line in the document. With // three, it iterates over the range given by the first two (with // the second being non-inclusive). iter: function(from, to, op) { if (op) { this.iterN(from - this.first, to - from, op); } else { this.iterN(this.first, this.first + this.size, from); } }, // Non-public interface for adding and removing lines. insert: function(at, lines) { var height = 0; for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } this.insertInner(at - this.first, lines, height); }, remove: function(at, n) { this.removeInner(at - this.first, n); }, // From here, the methods are part of the public interface. Most // are also available from CodeMirror (editor) instances. getValue: function(lineSep) { var lines = getLines(this, this.first, this.first + this.size); if (lineSep === false) { return lines } return lines.join(lineSep || this.lineSeparator()) }, setValue: docMethodOp(function(code) { var top = Pos(this.first, 0), last = this.first + this.size - 1; makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: this.splitLines(code), origin: "setValue", full: true}, true); if (this.cm) { scrollToCoords(this.cm, 0, 0); } setSelection(this, simpleSelection(top), sel_dontScroll); }), replaceRange: function(code, from, to, origin) { from = clipPos(this, from); to = to ? clipPos(this, to) : from; replaceRange(this, code, from, to, origin); }, getRange: function(from, to, lineSep) { var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); if (lineSep === false) { return lines } return lines.join(lineSep || this.lineSeparator()) }, getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, getLineNumber: function(line) {return lineNo(line)}, getLineHandleVisualStart: function(line) { if (typeof line == "number") { line = getLine(this, line); } return visualLine(line) }, lineCount: function() {return this.size}, firstLine: function() {return this.first}, lastLine: function() {return this.first + this.size - 1}, clipPos: function(pos) {return clipPos(this, pos)}, getCursor: function(start) { var range$$1 = this.sel.primary(), pos; if (start == null || start == "head") { pos = range$$1.head; } else if (start == "anchor") { pos = range$$1.anchor; } else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); } else { pos = range$$1.from(); } return pos }, listSelections: function() { return this.sel.ranges }, somethingSelected: function() {return this.sel.somethingSelected()}, setCursor: docMethodOp(function(line, ch, options) { setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); }), setSelection: docMethodOp(function(anchor, head, options) { setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); }), extendSelection: docMethodOp(function(head, other, options) { extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); }), extendSelections: docMethodOp(function(heads, options) { extendSelections(this, clipPosArray(this, heads), options); }), extendSelectionsBy: docMethodOp(function(f, options) { var heads = map(this.sel.ranges, f); extendSelections(this, clipPosArray(this, heads), options); }), setSelections: docMethodOp(function(ranges, primary, options) { var this$1 = this; if (!ranges.length) { return } var out = []; for (var i = 0; i < ranges.length; i++) { out[i] = new Range(clipPos(this$1, ranges[i].anchor), clipPos(this$1, ranges[i].head)); } if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } setSelection(this, normalizeSelection(this.cm, out, primary), options); }), addSelection: docMethodOp(function(anchor, head, options) { var ranges = this.sel.ranges.slice(0); ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); }), getSelection: function(lineSep) { var this$1 = this; var ranges = this.sel.ranges, lines; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); lines = lines ? lines.concat(sel) : sel; } if (lineSep === false) { return lines } else { return lines.join(lineSep || this.lineSeparator()) } }, getSelections: function(lineSep) { var this$1 = this; var parts = [], ranges = this.sel.ranges; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); } parts[i] = sel; } return parts }, replaceSelection: function(code, collapse, origin) { var dup = []; for (var i = 0; i < this.sel.ranges.length; i++) { dup[i] = code; } this.replaceSelections(dup, collapse, origin || "+input"); }, replaceSelections: docMethodOp(function(code, collapse, origin) { var this$1 = this; var changes = [], sel = this.sel; for (var i = 0; i < sel.ranges.length; i++) { var range$$1 = sel.ranges[i]; changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}; } var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) { makeChange(this$1, changes[i$1]); } if (newSel) { setSelectionReplaceHistory(this, newSel); } else if (this.cm) { ensureCursorVisible(this.cm); } }), undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), setExtending: function(val) {this.extend = val;}, getExtending: function() {return this.extend}, historySize: function() { var hist = this.history, done = 0, undone = 0; for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } return {undo: done, redo: undone} }, clearHistory: function() {this.history = new History(this.history.maxGeneration);}, markClean: function() { this.cleanGeneration = this.changeGeneration(true); }, changeGeneration: function(forceSplit) { if (forceSplit) { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } return this.history.generation }, isClean: function (gen) { return this.history.generation == (gen || this.cleanGeneration) }, getHistory: function() { return {done: copyHistoryArray(this.history.done), undone: copyHistoryArray(this.history.undone)} }, setHistory: function(histData) { var hist = this.history = new History(this.history.maxGeneration); hist.done = copyHistoryArray(histData.done.slice(0), null, true); hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); }, setGutterMarker: docMethodOp(function(line, gutterID, value) { return changeLine(this, line, "gutter", function (line) { var markers = line.gutterMarkers || (line.gutterMarkers = {}); markers[gutterID] = value; if (!value && isEmpty(markers)) { line.gutterMarkers = null; } return true }) }), clearGutter: docMethodOp(function(gutterID) { var this$1 = this; this.iter(function (line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { changeLine(this$1, line, "gutter", function () { line.gutterMarkers[gutterID] = null; if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } return true }); } }); }), lineInfo: function(line) { var n; if (typeof line == "number") { if (!isLine(this, line)) { return null } n = line; line = getLine(this, line); if (!line) { return null } } else { n = lineNo(line); if (n == null) { return null } } return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, widgets: line.widgets} }, addLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; if (!line[prop]) { line[prop] = cls; } else if (classTest(cls).test(line[prop])) { return false } else { line[prop] += " " + cls; } return true }) }), removeLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; var cur = line[prop]; if (!cur) { return false } else if (cls == null) { line[prop] = null; } else { var found = cur.match(classTest(cls)); if (!found) { return false } var end = found.index + found[0].length; line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; } return true }) }), addLineWidget: docMethodOp(function(handle, node, options) { return addLineWidget(this, handle, node, options) }), removeLineWidget: function(widget) { widget.clear(); }, markText: function(from, to, options) { return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") }, setBookmark: function(pos, options) { var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), insertLeft: options && options.insertLeft, clearWhenEmpty: false, shared: options && options.shared, handleMouseEvents: options && options.handleMouseEvents}; pos = clipPos(this, pos); return markText(this, pos, pos, realOpts, "bookmark") }, findMarksAt: function(pos) { pos = clipPos(this, pos); var markers = [], spans = getLine(this, pos.line).markedSpans; if (spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if ((span.from == null || span.from <= pos.ch) && (span.to == null || span.to >= pos.ch)) { markers.push(span.marker.parent || span.marker); } } } return markers }, findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to); var found = [], lineNo$$1 = from.line; this.iter(from.line, to.line + 1, function (line) { var spans = line.markedSpans; if (spans) { for (var i = 0; i < spans.length; i++) { var span = spans[i]; if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to || span.from == null && lineNo$$1 != from.line || span.from != null && lineNo$$1 == to.line && span.from >= to.ch) && (!filter || filter(span.marker))) { found.push(span.marker.parent || span.marker); } } } ++lineNo$$1; }); return found }, getAllMarks: function() { var markers = []; this.iter(function (line) { var sps = line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { if (sps[i].from != null) { markers.push(sps[i].marker); } } } }); return markers }, posFromIndex: function(off) { var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length; this.iter(function (line) { var sz = line.text.length + sepSize; if (sz > off) { ch = off; return true } off -= sz; ++lineNo$$1; }); return clipPos(this, Pos(lineNo$$1, ch)) }, indexFromPos: function (coords) { coords = clipPos(this, coords); var index = coords.ch; if (coords.line < this.first || coords.ch < 0) { return 0 } var sepSize = this.lineSeparator().length; this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value index += line.text.length + sepSize; }); return index }, copy: function(copyHistory) { var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first, this.lineSep, this.direction); doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; doc.sel = this.sel; doc.extend = false; if (copyHistory) { doc.history.undoDepth = this.history.undoDepth; doc.setHistory(this.getHistory()); } return doc }, linkedDoc: function(options) { if (!options) { options = {}; } var from = this.first, to = this.first + this.size; if (options.from != null && options.from > from) { from = options.from; } if (options.to != null && options.to < to) { to = options.to; } var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); if (options.sharedHist) { copy.history = this.history ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; copySharedMarkers(copy, findSharedMarkers(this)); return copy }, unlinkDoc: function(other) { var this$1 = this; if (other instanceof CodeMirror) { other = other.doc; } if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { var link = this$1.linked[i]; if (link.doc != other) { continue } this$1.linked.splice(i, 1); other.unlinkDoc(this$1); detachSharedMarkers(findSharedMarkers(this$1)); break } } // If the histories were shared, split them again if (other.history == this.history) { var splitIds = [other.id]; linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); other.history = new History(null); other.history.done = copyHistoryArray(this.history.done, splitIds); other.history.undone = copyHistoryArray(this.history.undone, splitIds); } }, iterLinkedDocs: function(f) {linkedDocs(this, f);}, getMode: function() {return this.mode}, getEditor: function() {return this.cm}, splitLines: function(str) { if (this.lineSep) { return str.split(this.lineSep) } return splitLinesAuto(str) }, lineSeparator: function() { return this.lineSep || "\n" }, setDirection: docMethodOp(function (dir) { if (dir != "rtl") { dir = "ltr"; } if (dir == this.direction) { return } this.direction = dir; this.iter(function (line) { return line.order = null; }); if (this.cm) { directionChanged(this.cm); } }) }); // Public alias. Doc.prototype.eachLine = Doc.prototype.iter; // Kludge to work around strange IE behavior where it'll sometimes // re-fire a series of drag-related events right after the drop (#1551) var lastDrop = 0; function onDrop(e) { var cm = this; clearDragCursor(cm); if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } e_preventDefault(e); if (ie) { lastDrop = +new Date; } var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; if (!pos || cm.isReadOnly()) { return } // Might be a file drop, in which case we simply extract the text // and insert it. if (files && files.length && window.FileReader && window.File) { var n = files.length, text = Array(n), read = 0; var loadFile = function (file, i) { if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) { return } var reader = new FileReader; reader.onload = operation(cm, function () { var content = reader.result; if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; } text[i] = content; if (++read == n) { pos = clipPos(cm.doc, pos); var change = {from: pos, to: pos, text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), origin: "paste"}; makeChange(cm.doc, change); setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); } }); reader.readAsText(file); }; for (var i = 0; i < n; ++i) { loadFile(files[i], i); } } else { // Normal drop // Don't do a replace if the drop happened inside of the selected text. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { cm.state.draggingText(e); // Ensure the editor is re-focused setTimeout(function () { return cm.display.input.focus(); }, 20); return } try { var text$1 = e.dataTransfer.getData("Text"); if (text$1) { var selected; if (cm.state.draggingText && !cm.state.draggingText.copy) { selected = cm.listSelections(); } setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } cm.replaceSelection(text$1, "around", "paste"); cm.display.input.focus(); } } catch(e){} } } function onDragStart(cm, e) { if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } e.dataTransfer.setData("Text", cm.getSelection()); e.dataTransfer.effectAllowed = "copyMove"; // Use dummy image instead of default browsers image. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. if (e.dataTransfer.setDragImage && !safari) { var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; if (presto) { img.width = img.height = 1; cm.display.wrapper.appendChild(img); // Force a relayout, or Opera won't use our image for some obscure reason img._top = img.offsetTop; } e.dataTransfer.setDragImage(img, 0, 0); if (presto) { img.parentNode.removeChild(img); } } } function onDragOver(cm, e) { var pos = posFromMouse(cm, e); if (!pos) { return } var frag = document.createDocumentFragment(); drawSelectionCursor(cm, pos, frag); if (!cm.display.dragCursor) { cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); } removeChildrenAndAdd(cm.display.dragCursor, frag); } function clearDragCursor(cm) { if (cm.display.dragCursor) { cm.display.lineSpace.removeChild(cm.display.dragCursor); cm.display.dragCursor = null; } } // These must be handled carefully, because naively registering a // handler for each editor will cause the editors to never be // garbage collected. function forEachCodeMirror(f) { if (!document.getElementsByClassName) { return } var byClass = document.getElementsByClassName("CodeMirror"), editors = []; for (var i = 0; i < byClass.length; i++) { var cm = byClass[i].CodeMirror; if (cm) { editors.push(cm); } } if (editors.length) { editors[0].operation(function () { for (var i = 0; i < editors.length; i++) { f(editors[i]); } }); } } var globalsRegistered = false; function ensureGlobalHandlers() { if (globalsRegistered) { return } registerGlobalHandlers(); globalsRegistered = true; } function registerGlobalHandlers() { // When the window resizes, we need to refresh active editors. var resizeTimer; on(window, "resize", function () { if (resizeTimer == null) { resizeTimer = setTimeout(function () { resizeTimer = null; forEachCodeMirror(onResize); }, 100); } }); // When the window loses focus, we want to show the editor as blurred on(window, "blur", function () { return forEachCodeMirror(onBlur); }); } // Called when the window resizes function onResize(cm) { var d = cm.display; // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; d.scrollbarsClipped = false; cm.setSize(); } var keyNames = { 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" }; // Number keys for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } // Alphabetic keys for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } // Function keys for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } var keyMap = {}; keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto", "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", "Esc": "singleSelection" }; // Note that the save and find-related commands aren't defined by // default. User code or addons can define them. Unknown commands // are simply ignored. keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", "fallthrough": "basic" }; // Very basic readline/emacs-style bindings, which are standard on Mac. keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", "fallthrough": ["basic", "emacsy"] }; keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; // KEYMAP DISPATCH function normalizeKeyName(name) { var parts = name.split(/-(?!$)/); name = parts[parts.length - 1]; var alt, ctrl, shift, cmd; for (var i = 0; i < parts.length - 1; i++) { var mod = parts[i]; if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } else if (/^a(lt)?$/i.test(mod)) { alt = true; } else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } else if (/^s(hift)?$/i.test(mod)) { shift = true; } else { throw new Error("Unrecognized modifier name: " + mod) } } if (alt) { name = "Alt-" + name; } if (ctrl) { name = "Ctrl-" + name; } if (cmd) { name = "Cmd-" + name; } if (shift) { name = "Shift-" + name; } return name } // This is a kludge to keep keymaps mostly working as raw objects // (backwards compatibility) while at the same time support features // like normalization and multi-stroke key bindings. It compiles a // new normalized keymap, and then updates the old object to reflect // this. function normalizeKeyMap(keymap) { var copy = {}; for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { var value = keymap[keyname]; if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } if (value == "...") { delete keymap[keyname]; continue } var keys = map(keyname.split(" "), normalizeKeyName); for (var i = 0; i < keys.length; i++) { var val = (void 0), name = (void 0); if (i == keys.length - 1) { name = keys.join(" "); val = value; } else { name = keys.slice(0, i + 1).join(" "); val = "..."; } var prev = copy[name]; if (!prev) { copy[name] = val; } else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } } delete keymap[keyname]; } } for (var prop in copy) { keymap[prop] = copy[prop]; } return keymap } function lookupKey(key, map$$1, handle, context) { map$$1 = getKeyMap(map$$1); var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]; if (found === false) { return "nothing" } if (found === "...") { return "multi" } if (found != null && handle(found)) { return "handled" } if (map$$1.fallthrough) { if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]") { return lookupKey(key, map$$1.fallthrough, handle, context) } for (var i = 0; i < map$$1.fallthrough.length; i++) { var result = lookupKey(key, map$$1.fallthrough[i], handle, context); if (result) { return result } } } } // Modifier key presses don't count as 'real' key presses for the // purpose of keymap fallthrough. function isModifierKey(value) { var name = typeof value == "string" ? value : keyNames[value.keyCode]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" } function addModifierNames(name, event, noShift) { var base = name; if (event.altKey && base != "Alt") { name = "Alt-" + name; } if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; } if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } return name } // Look up the name of a key as indicated by an event object. function keyName(event, noShift) { if (presto && event.keyCode == 34 && event["char"]) { return false } var name = keyNames[event.keyCode]; if (name == null || event.altGraphKey) { return false } // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) if (event.keyCode == 3 && event.code) { name = event.code; } return addModifierNames(name, event, noShift) } function getKeyMap(val) { return typeof val == "string" ? keyMap[val] : val } // Helper for deleting text near the selection(s), used to implement // backspace, delete, and similar functionality. function deleteNearSelection(cm, compute) { var ranges = cm.doc.sel.ranges, kill = []; // Build up a set of ranges to kill first, merging overlapping // ranges. for (var i = 0; i < ranges.length; i++) { var toKill = compute(ranges[i]); while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { var replaced = kill.pop(); if (cmp(replaced.from, toKill.from) < 0) { toKill.from = replaced.from; break } } kill.push(toKill); } // Next, remove those actual ranges. runInOp(cm, function () { for (var i = kill.length - 1; i >= 0; i--) { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } ensureCursorVisible(cm); }); } function moveCharLogically(line, ch, dir) { var target = skipExtendingChars(line.text, ch + dir, dir); return target < 0 || target > line.text.length ? null : target } function moveLogically(line, start, dir) { var ch = moveCharLogically(line, start.ch, dir); return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") } function endOfLine(visually, cm, lineObj, lineNo, dir) { if (visually) { var order = getOrder(lineObj, cm.doc.direction); if (order) { var part = dir < 0 ? lst(order) : order[0]; var moveInStorageOrder = (dir < 0) == (part.level == 1); var sticky = moveInStorageOrder ? "after" : "before"; var ch; // With a wrapped rtl chunk (possibly spanning multiple bidi parts), // it could be that the last bidi part is not on the last visual line, // since visual lines contain content order-consecutive chunks. // Thus, in rtl, we are looking for the first (content-order) character // in the rtl chunk that is on the last line (that is, the same line // as the last (content-order) character). if (part.level > 0 || cm.doc.direction == "rtl") { var prep = prepareMeasureForLine(cm, lineObj); ch = dir < 0 ? lineObj.text.length - 1 : 0; var targetTop = measureCharPrepared(cm, prep, ch).top; ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } } else { ch = dir < 0 ? part.to : part.from; } return new Pos(lineNo, ch, sticky) } } return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") } function moveVisually(cm, line, start, dir) { var bidi = getOrder(line, cm.doc.direction); if (!bidi) { return moveLogically(line, start, dir) } if (start.ch >= line.text.length) { start.ch = line.text.length; start.sticky = "before"; } else if (start.ch <= 0) { start.ch = 0; start.sticky = "after"; } var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, // nothing interesting happens. return moveLogically(line, start, dir) } var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; var prep; var getWrappedLineExtent = function (ch) { if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } prep = prep || prepareMeasureForLine(cm, line); return wrappedLineExtentChar(cm, line, prep, ch) }; var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); if (cm.doc.direction == "rtl" || part.level == 1) { var moveInStorageOrder = (part.level == 1) == (dir < 0); var ch = mv(start, moveInStorageOrder ? 1 : -1); if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { // Case 2: We move within an rtl part or in an rtl editor on the same visual line var sticky = moveInStorageOrder ? "before" : "after"; return new Pos(start.line, ch, sticky) } } // Case 3: Could not move within this bidi part in this visual line, so leave // the current bidi part var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder ? new Pos(start.line, mv(ch, 1), "before") : new Pos(start.line, ch, "after"); }; for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { var part = bidi[partPos]; var moveInStorageOrder = (dir > 0) == (part.level != 1); var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } ch = moveInStorageOrder ? part.from : mv(part.to, -1); if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } } }; // Case 3a: Look for other bidi parts on the same visual line var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); if (res) { return res } // Case 3b: Look for other bidi parts on the next visual line var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); if (res) { return res } } // Case 4: Nowhere to move return null } // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. var commands = { selectAll: selectAll, singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, killLine: function (cm) { return deleteNearSelection(cm, function (range) { if (range.empty()) { var len = getLine(cm.doc, range.head.line).text.length; if (range.head.ch == len && range.head.line < cm.lastLine()) { return {from: range.head, to: Pos(range.head.line + 1, 0)} } else { return {from: range.head, to: Pos(range.head.line, len)} } } else { return {from: range.from(), to: range.to()} } }); }, deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ from: Pos(range.from().line, 0), to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) }); }); }, delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ from: Pos(range.from().line, 0), to: range.from() }); }); }, delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { var top = cm.charCoords(range.head, "div").top + 5; var leftPos = cm.coordsChar({left: 0, top: top}, "div"); return {from: leftPos, to: range.from()} }); }, delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { var top = cm.charCoords(range.head, "div").top + 5; var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); return {from: range.from(), to: rightPos } }); }, undo: function (cm) { return cm.undo(); }, redo: function (cm) { return cm.redo(); }, undoSelection: function (cm) { return cm.undoSelection(); }, redoSelection: function (cm) { return cm.redoSelection(); }, goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, {origin: "+move", bias: 1} ); }, goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, {origin: "+move", bias: 1} ); }, goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, {origin: "+move", bias: -1} ); }, goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") }, sel_move); }, goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; return cm.coordsChar({left: 0, top: top}, "div") }, sel_move); }, goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.cursorCoords(range.head, "div").top + 5; var pos = cm.coordsChar({left: 0, top: top}, "div"); if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } return pos }, sel_move); }, goLineUp: function (cm) { return cm.moveV(-1, "line"); }, goLineDown: function (cm) { return cm.moveV(1, "line"); }, goPageUp: function (cm) { return cm.moveV(-1, "page"); }, goPageDown: function (cm) { return cm.moveV(1, "page"); }, goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, goCharRight: function (cm) { return cm.moveH(1, "char"); }, goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, goColumnRight: function (cm) { return cm.moveH(1, "column"); }, goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, goGroupRight: function (cm) { return cm.moveH(1, "group"); }, goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, goWordRight: function (cm) { return cm.moveH(1, "word"); }, delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, indentAuto: function (cm) { return cm.indentSelection("smart"); }, indentMore: function (cm) { return cm.indentSelection("add"); }, indentLess: function (cm) { return cm.indentSelection("subtract"); }, insertTab: function (cm) { return cm.replaceSelection("\t"); }, insertSoftTab: function (cm) { var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; for (var i = 0; i < ranges.length; i++) { var pos = ranges[i].from(); var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); spaces.push(spaceStr(tabSize - col % tabSize)); } cm.replaceSelections(spaces); }, defaultTab: function (cm) { if (cm.somethingSelected()) { cm.indentSelection("add"); } else { cm.execCommand("insertTab"); } }, // Swap the two chars left and right of each selection's head. // Move cursor behind the two swapped characters afterwards. // // Doesn't consider line feeds a character. // Doesn't scan more than one line above to find a character. // Doesn't do anything on an empty line. // Doesn't do anything with non-empty selections. transposeChars: function (cm) { return runInOp(cm, function () { var ranges = cm.listSelections(), newSel = []; for (var i = 0; i < ranges.length; i++) { if (!ranges[i].empty()) { continue } var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; if (line) { if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } if (cur.ch > 0) { cur = new Pos(cur.line, cur.ch + 1); cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), Pos(cur.line, cur.ch - 2), cur, "+transpose"); } else if (cur.line > cm.doc.first) { var prev = getLine(cm.doc, cur.line - 1).text; if (prev) { cur = new Pos(cur.line, 1); cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + prev.charAt(prev.length - 1), Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); } } } newSel.push(new Range(cur, cur)); } cm.setSelections(newSel); }); }, newlineAndIndent: function (cm) { return runInOp(cm, function () { var sels = cm.listSelections(); for (var i = sels.length - 1; i >= 0; i--) { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } sels = cm.listSelections(); for (var i$1 = 0; i$1 < sels.length; i$1++) { cm.indentLine(sels[i$1].from().line, null, true); } ensureCursorVisible(cm); }); }, openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } }; function lineStart(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLine(line); if (visual != line) { lineN = lineNo(visual); } return endOfLine(true, cm, visual, lineN, 1) } function lineEnd(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLineEnd(line); if (visual != line) { lineN = lineNo(visual); } return endOfLine(true, cm, line, lineN, -1) } function lineStartSmart(cm, pos) { var start = lineStart(cm, pos.line); var line = getLine(cm.doc, start.line); var order = getOrder(line, cm.doc.direction); if (!order || order[0].level == 0) { var firstNonWS = Math.max(0, line.text.search(/\S/)); var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) } return start } // Run a handler that was bound to a key. function doHandleBinding(cm, bound, dropShift) { if (typeof bound == "string") { bound = commands[bound]; if (!bound) { return false } } // Ensure previous input has been read, so that the handler sees a // consistent view of the document cm.display.input.ensurePolled(); var prevShift = cm.display.shift, done = false; try { if (cm.isReadOnly()) { cm.state.suppressEdits = true; } if (dropShift) { cm.display.shift = false; } done = bound(cm) != Pass; } finally { cm.display.shift = prevShift; cm.state.suppressEdits = false; } return done } function lookupKeyForEditor(cm, name, handle) { for (var i = 0; i < cm.state.keyMaps.length; i++) { var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); if (result) { return result } } return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) || lookupKey(name, cm.options.keyMap, handle, cm) } // Note that, despite the name, this function is also used to check // for bound mouse clicks. var stopSeq = new Delayed; function dispatchKey(cm, name, e, handle) { var seq = cm.state.keySeq; if (seq) { if (isModifierKey(name)) { return "handled" } if (/\'$/.test(name)) { cm.state.keySeq = null; } else { stopSeq.set(50, function () { if (cm.state.keySeq == seq) { cm.state.keySeq = null; cm.display.input.reset(); } }); } if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } } return dispatchKeyInner(cm, name, e, handle) } function dispatchKeyInner(cm, name, e, handle) { var result = lookupKeyForEditor(cm, name, handle); if (result == "multi") { cm.state.keySeq = name; } if (result == "handled") { signalLater(cm, "keyHandled", cm, name, e); } if (result == "handled" || result == "multi") { e_preventDefault(e); restartBlink(cm); } return !!result } // Handle a key from the keydown event. function handleKeyBinding(cm, e) { var name = keyName(e, true); if (!name) { return false } if (e.shiftKey && !cm.state.keySeq) { // First try to resolve full name (including 'Shift-'). Failing // that, see if there is a cursor-motion command (starting with // 'go') bound to the keyname without 'Shift-'. return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) || dispatchKey(cm, name, e, function (b) { if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) { return doHandleBinding(cm, b) } }) } else { return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) } } // Handle a key from the keypress event function handleCharBinding(cm, e, ch) { return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) } var lastStoppedKey = null; function onKeyDown(e) { var cm = this; cm.curOp.focus = activeElt(); if (signalDOMEvent(cm, e)) { return } // IE does strange things with escape. if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } var code = e.keyCode; cm.display.shift = code == 16 || e.shiftKey; var handled = handleKeyBinding(cm, e); if (presto) { lastStoppedKey = handled ? code : null; // Opera has no cut event... we try to at least catch the key combo if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) { cm.replaceSelection("", null, "cut"); } } // Turn mouse into crosshair when Alt is held on Mac. if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) { showCrossHair(cm); } } function showCrossHair(cm) { var lineDiv = cm.display.lineDiv; addClass(lineDiv, "CodeMirror-crosshair"); function up(e) { if (e.keyCode == 18 || !e.altKey) { rmClass(lineDiv, "CodeMirror-crosshair"); off(document, "keyup", up); off(document, "mouseover", up); } } on(document, "keyup", up); on(document, "mouseover", up); } function onKeyUp(e) { if (e.keyCode == 16) { this.doc.sel.shift = false; } signalDOMEvent(this, e); } function onKeyPress(e) { var cm = this; if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } var keyCode = e.keyCode, charCode = e.charCode; if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } var ch = String.fromCharCode(charCode == null ? keyCode : charCode); // Some browsers fire keypress events for backspace if (ch == "\x08") { return } if (handleCharBinding(cm, e, ch)) { return } cm.display.input.onKeyPress(e); } var DOUBLECLICK_DELAY = 400; var PastClick = function(time, pos, button) { this.time = time; this.pos = pos; this.button = button; }; PastClick.prototype.compare = function (time, pos, button) { return this.time + DOUBLECLICK_DELAY > time && cmp(pos, this.pos) == 0 && button == this.button }; var lastClick, lastDoubleClick; function clickRepeat(pos, button) { var now = +new Date; if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { lastClick = lastDoubleClick = null; return "triple" } else if (lastClick && lastClick.compare(now, pos, button)) { lastDoubleClick = new PastClick(now, pos, button); lastClick = null; return "double" } else { lastClick = new PastClick(now, pos, button); lastDoubleClick = null; return "single" } } // A mouse down can be a single click, double click, triple click, // start of selection drag, start of text drag, new cursor // (ctrl-click), rectangle drag (alt-drag), or xwin // middle-click-paste. Or it might be a click on something we should // not interfere with, such as a scrollbar or widget. function onMouseDown(e) { var cm = this, display = cm.display; if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } display.input.ensurePolled(); display.shift = e.shiftKey; if (eventInWidget(display, e)) { if (!webkit) { // Briefly turn off draggability, to allow widgets to do // normal dragging things. display.scroller.draggable = false; setTimeout(function () { return display.scroller.draggable = true; }, 100); } return } if (clickInGutter(cm, e)) { return } var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; window.focus(); // #3261: make sure, that we're not starting a second selection if (button == 1 && cm.state.selectingText) { cm.state.selectingText(e); } if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } if (button == 1) { if (pos) { leftButtonDown(cm, pos, repeat, e); } else if (e_target(e) == display.scroller) { e_preventDefault(e); } } else if (button == 2) { if (pos) { extendSelection(cm.doc, pos); } setTimeout(function () { return display.input.focus(); }, 20); } else if (button == 3) { if (captureRightClick) { cm.display.input.onContextMenu(e); } else { delayBlurEvent(cm); } } } function handleMappedButton(cm, button, pos, repeat, event) { var name = "Click"; if (repeat == "double") { name = "Double" + name; } else if (repeat == "triple") { name = "Triple" + name; } name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { if (typeof bound == "string") { bound = commands[bound]; } if (!bound) { return false } var done = false; try { if (cm.isReadOnly()) { cm.state.suppressEdits = true; } done = bound(cm, pos) != Pass; } finally { cm.state.suppressEdits = false; } return done }) } function configureMouse(cm, repeat, event) { var option = cm.getOption("configureMouse"); var value = option ? option(cm, repeat, event) : {}; if (value.unit == null) { var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; } if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } return value } function leftButtonDown(cm, pos, repeat, event) { if (ie) { setTimeout(bind(ensureFocus, cm), 0); } else { cm.curOp.focus = activeElt(); } var behavior = configureMouse(cm, repeat, event); var sel = cm.doc.sel, contained; if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && repeat == "single" && (contained = sel.contains(pos)) > -1 && (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) { leftButtonStartDrag(cm, event, pos, behavior); } else { leftButtonSelect(cm, event, pos, behavior); } } // Start a text drag. When it ends, see if any dragging actually // happen, and treat as a click if it didn't. function leftButtonStartDrag(cm, event, pos, behavior) { var display = cm.display, moved = false; var dragEnd = operation(cm, function (e) { if (webkit) { display.scroller.draggable = false; } cm.state.draggingText = false; off(display.wrapper.ownerDocument, "mouseup", dragEnd); off(display.wrapper.ownerDocument, "mousemove", mouseMove); off(display.scroller, "dragstart", dragStart); off(display.scroller, "drop", dragEnd); if (!moved) { e_preventDefault(e); if (!behavior.addNew) { extendSelection(cm.doc, pos, null, null, behavior.extend); } // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) if (webkit || ie && ie_version == 9) { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); } else { display.input.focus(); } } }); var mouseMove = function(e2) { moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; }; var dragStart = function () { return moved = true; }; // Let the drag handler handle this. if (webkit) { display.scroller.draggable = true; } cm.state.draggingText = dragEnd; dragEnd.copy = !behavior.moveOnDrag; // IE's approach to draggable if (display.scroller.dragDrop) { display.scroller.dragDrop(); } on(display.wrapper.ownerDocument, "mouseup", dragEnd); on(display.wrapper.ownerDocument, "mousemove", mouseMove); on(display.scroller, "dragstart", dragStart); on(display.scroller, "drop", dragEnd); delayBlurEvent(cm); setTimeout(function () { return display.input.focus(); }, 20); } function rangeForUnit(cm, pos, unit) { if (unit == "char") { return new Range(pos, pos) } if (unit == "word") { return cm.findWordAt(pos) } if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } var result = unit(cm, pos); return new Range(result.from, result.to) } // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, event, start, behavior) { var display = cm.display, doc = cm.doc; e_preventDefault(event); var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; if (behavior.addNew && !behavior.extend) { ourIndex = doc.sel.contains(start); if (ourIndex > -1) { ourRange = ranges[ourIndex]; } else { ourRange = new Range(start, start); } } else { ourRange = doc.sel.primary(); ourIndex = doc.sel.primIndex; } if (behavior.unit == "rectangle") { if (!behavior.addNew) { ourRange = new Range(start, start); } start = posFromMouse(cm, event, true, true); ourIndex = -1; } else { var range$$1 = rangeForUnit(cm, start, behavior.unit); if (behavior.extend) { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); } else { ourRange = range$$1; } } if (!behavior.addNew) { ourIndex = 0; setSelection(doc, new Selection([ourRange], 0), sel_mouse); startSel = doc.sel; } else if (ourIndex == -1) { ourIndex = ranges.length; setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), {scroll: false, origin: "*mouse"}); } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), {scroll: false, origin: "*mouse"}); startSel = doc.sel; } else { replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); } var lastPos = start; function extendTo(pos) { if (cmp(lastPos, pos) == 0) { return } lastPos = pos; if (behavior.unit == "rectangle") { var ranges = [], tabSize = cm.options.tabSize; var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); line <= end; line++) { var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); if (left == right) { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } else if (text.length > leftPos) { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } } if (!ranges.length) { ranges.push(new Range(start, start)); } setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), {origin: "*mouse", scroll: false}); cm.scrollIntoView(pos); } else { var oldRange = ourRange; var range$$1 = rangeForUnit(cm, pos, behavior.unit); var anchor = oldRange.anchor, head; if (cmp(range$$1.anchor, anchor) > 0) { head = range$$1.head; anchor = minPos(oldRange.from(), range$$1.anchor); } else { head = range$$1.anchor; anchor = maxPos(oldRange.to(), range$$1.head); } var ranges$1 = startSel.ranges.slice(0); ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); } } var editorSize = display.wrapper.getBoundingClientRect(); // Used to ensure timeout re-tries don't fire when another extend // happened in the meantime (clearTimeout isn't reliable -- at // least on Chrome, the timeouts still happen even when cleared, // if the clear happens after their scheduled firing time). var counter = 0; function extend(e) { var curCount = ++counter; var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); if (!cur) { return } if (cmp(cur, lastPos) != 0) { cm.curOp.focus = activeElt(); extendTo(cur); var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } } else { var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; if (outside) { setTimeout(operation(cm, function () { if (counter != curCount) { return } display.scroller.scrollTop += outside; extend(e); }), 50); } } } function done(e) { cm.state.selectingText = false; counter = Infinity; // If e is null or undefined we interpret this as someone trying // to explicitly cancel the selection rather than the user // letting go of the mouse button. if (e) { e_preventDefault(e); display.input.focus(); } off(display.wrapper.ownerDocument, "mousemove", move); off(display.wrapper.ownerDocument, "mouseup", up); doc.history.lastSelOrigin = null; } var move = operation(cm, function (e) { if (e.buttons === 0 || !e_button(e)) { done(e); } else { extend(e); } }); var up = operation(cm, done); cm.state.selectingText = up; on(display.wrapper.ownerDocument, "mousemove", move); on(display.wrapper.ownerDocument, "mouseup", up); } // Used when mouse-selecting to adjust the anchor to the proper side // of a bidi jump depending on the visual position of the head. function bidiSimplify(cm, range$$1) { var anchor = range$$1.anchor; var head = range$$1.head; var anchorLine = getLine(cm.doc, anchor.line); if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 } var order = getOrder(anchorLine); if (!order) { return range$$1 } var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 } var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); if (boundary == 0 || boundary == order.length) { return range$$1 } // Compute the relative visual position of the head compared to the // anchor (<0 is to the left, >0 to the right) var leftSide; if (head.line != anchor.line) { leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; } else { var headIndex = getBidiPartAt(order, head.ch, head.sticky); var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); if (headIndex == boundary - 1 || headIndex == boundary) { leftSide = dir < 0; } else { leftSide = dir > 0; } } var usePart = order[boundary + (leftSide ? -1 : 0)]; var from = leftSide == (usePart.level == 1); var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head) } // Determines whether an event happened in the gutter, and fires the // handlers for the corresponding event. function gutterEvent(cm, e, type, prevent) { var mX, mY; if (e.touches) { mX = e.touches[0].clientX; mY = e.touches[0].clientY; } else { try { mX = e.clientX; mY = e.clientY; } catch(e) { return false } } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } if (prevent) { e_preventDefault(e); } var display = cm.display; var lineBox = display.lineDiv.getBoundingClientRect(); if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } mY -= lineBox.top - display.viewOffset; for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { var g = display.gutters.childNodes[i]; if (g && g.getBoundingClientRect().right >= mX) { var line = lineAtHeight(cm.doc, mY); var gutter = cm.display.gutterSpecs[i]; signal(cm, type, cm, line, gutter.className, e); return e_defaultPrevented(e) } } } function clickInGutter(cm, e) { return gutterEvent(cm, e, "gutterClick", true) } // CONTEXT MENU HANDLING // To make the context menu work, we need to briefly unhide the // textarea (making it as unobtrusive as possible) to let the // right-click take effect on it. function onContextMenu(cm, e) { if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } if (signalDOMEvent(cm, e, "contextmenu")) { return } if (!captureRightClick) { cm.display.input.onContextMenu(e); } } function contextMenuInGutter(cm, e) { if (!hasHandler(cm, "gutterContextMenu")) { return false } return gutterEvent(cm, e, "gutterContextMenu", false) } function themeChanged(cm) { cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); clearCaches(cm); } var Init = {toString: function(){return "CodeMirror.Init"}}; var defaults = {}; var optionHandlers = {}; function defineOptions(CodeMirror) { var optionHandlers = CodeMirror.optionHandlers; function option(name, deflt, handle, notOnInit) { CodeMirror.defaults[name] = deflt; if (handle) { optionHandlers[name] = notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } } CodeMirror.defineOption = option; // Passed to option handlers when there is no old value. CodeMirror.Init = Init; // These two are, on init, called from the constructor because they // have to be initialized before the editor can start at all. option("value", "", function (cm, val) { return cm.setValue(val); }, true); option("mode", null, function (cm, val) { cm.doc.modeOption = val; loadMode(cm); }, true); option("indentUnit", 2, loadMode, true); option("indentWithTabs", false); option("smartIndent", true); option("tabSize", 4, function (cm) { resetModeState(cm); clearCaches(cm); regChange(cm); }, true); option("lineSeparator", null, function (cm, val) { cm.doc.lineSep = val; if (!val) { return } var newBreaks = [], lineNo = cm.doc.first; cm.doc.iter(function (line) { for (var pos = 0;;) { var found = line.text.indexOf(val, pos); if (found == -1) { break } pos = found + val.length; newBreaks.push(Pos(lineNo, found)); } lineNo++; }); for (var i = newBreaks.length - 1; i >= 0; i--) { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } }); option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); if (old != Init) { cm.refresh(); } }); option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); option("electricChars", true); option("inputStyle", mobile ? "contenteditable" : "textarea", function () { throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME }, true); option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); option("rtlMoveVisually", !windows); option("wholeLineUpdateBefore", true); option("theme", "default", function (cm) { themeChanged(cm); updateGutters(cm); }, true); option("keyMap", "default", function (cm, val, old) { var next = getKeyMap(val); var prev = old != Init && getKeyMap(old); if (prev && prev.detach) { prev.detach(cm, next); } if (next.attach) { next.attach(cm, prev || null); } }); option("extraKeys", null); option("configureMouse", null); option("lineWrapping", false, wrappingChanged, true); option("gutters", [], function (cm, val) { cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); updateGutters(cm); }, true); option("fixedGutter", true, function (cm, val) { cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; cm.refresh(); }, true); option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); option("scrollbarStyle", "native", function (cm) { initScrollbars(cm); updateScrollbars(cm); cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); }, true); option("lineNumbers", false, function (cm, val) { cm.display.gutterSpecs = getGutters(cm.options.gutters, val); updateGutters(cm); }, true); option("firstLineNumber", 1, updateGutters, true); option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); option("showCursorWhenSelecting", false, updateSelection, true); option("resetSelectionOnContextMenu", true); option("lineWiseCopyCut", true); option("pasteLinesPerSelection", true); option("selectionsMayTouch", false); option("readOnly", false, function (cm, val) { if (val == "nocursor") { onBlur(cm); cm.display.input.blur(); } cm.display.input.readOnlyChanged(val); }); option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); option("dragDrop", true, dragDropChanged); option("allowDropFileTypes", null); option("cursorBlinkRate", 530); option("cursorScrollMargin", 0); option("cursorHeight", 1, updateSelection, true); option("singleCursorHeightPerLine", true, updateSelection, true); option("workTime", 100); option("workDelay", 100); option("flattenSpans", true, resetModeState, true); option("addModeClass", false, resetModeState, true); option("pollInterval", 100); option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); option("historyEventDelay", 1250); option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); option("maxHighlightLength", 10000, resetModeState, true); option("moveInputWithCursor", true, function (cm, val) { if (!val) { cm.display.input.resetPosition(); } }); option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); option("autofocus", null); option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); option("phrases", null); } function dragDropChanged(cm, value, old) { var wasOn = old && old != Init; if (!value != !wasOn) { var funcs = cm.display.dragFunctions; var toggle = value ? on : off; toggle(cm.display.scroller, "dragstart", funcs.start); toggle(cm.display.scroller, "dragenter", funcs.enter); toggle(cm.display.scroller, "dragover", funcs.over); toggle(cm.display.scroller, "dragleave", funcs.leave); toggle(cm.display.scroller, "drop", funcs.drop); } } function wrappingChanged(cm) { if (cm.options.lineWrapping) { addClass(cm.display.wrapper, "CodeMirror-wrap"); cm.display.sizer.style.minWidth = ""; cm.display.sizerWidth = null; } else { rmClass(cm.display.wrapper, "CodeMirror-wrap"); findMaxLine(cm); } estimateLineHeights(cm); regChange(cm); clearCaches(cm); setTimeout(function () { return updateScrollbars(cm); }, 100); } // A CodeMirror instance represents an editor. This is the object // that user code is usually dealing with. function CodeMirror(place, options) { var this$1 = this; if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } this.options = options = options ? copyObj(options) : {}; // Determine effective options based on given values and defaults. copyObj(defaults, options, false); var doc = options.value; if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } else if (options.mode) { doc.modeOption = options.mode; } this.doc = doc; var input = new CodeMirror.inputStyles[options.inputStyle](this); var display = this.display = new Display(place, doc, input, options); display.wrapper.CodeMirror = this; themeChanged(this); if (options.lineWrapping) { this.display.wrapper.className += " CodeMirror-wrap"; } initScrollbars(this); this.state = { keyMaps: [], // stores maps added by addKeyMap overlays: [], // highlighting overlays, as added by addOverlay modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info overwrite: false, delayingBlurEvent: false, focused: false, suppressEdits: false, // used to disable editing during key handlers when in readOnly mode pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll selectingText: false, draggingText: false, highlight: new Delayed(), // stores highlight worker timeout keySeq: null, // Unfinished key sequence specialChars: null }; if (options.autofocus && !mobile) { display.input.focus(); } // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } registerEventHandlers(this); ensureGlobalHandlers(); startOperation(this); this.curOp.forceUpdate = true; attachDoc(this, doc); if ((options.autofocus && !mobile) || this.hasFocus()) { setTimeout(bind(onFocus, this), 20); } else { onBlur(this); } for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) { optionHandlers[opt](this$1, options[opt], Init); } } maybeUpdateLineNumberWidth(this); if (options.finishInit) { options.finishInit(this); } for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); } endOperation(this); // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. if (webkit && options.lineWrapping && getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") { display.lineDiv.style.textRendering = "auto"; } } // The default configuration options. CodeMirror.defaults = defaults; // Functions to run when options are changed. CodeMirror.optionHandlers = optionHandlers; // Attach the necessary event handlers when initializing the editor function registerEventHandlers(cm) { var d = cm.display; on(d.scroller, "mousedown", operation(cm, onMouseDown)); // Older IE's will not fire a second mousedown for a double click if (ie && ie_version < 11) { on(d.scroller, "dblclick", operation(cm, function (e) { if (signalDOMEvent(cm, e)) { return } var pos = posFromMouse(cm, e); if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } e_preventDefault(e); var word = cm.findWordAt(pos); extendSelection(cm.doc, word.anchor, word.head); })); } else { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } // Some browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for these browsers. on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); // Used to suppress mouse event handling when a touch happens var touchFinished, prevTouch = {end: 0}; function finishTouch() { if (d.activeTouch) { touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); prevTouch = d.activeTouch; prevTouch.end = +new Date; } } function isMouseLikeTouchEvent(e) { if (e.touches.length != 1) { return false } var touch = e.touches[0]; return touch.radiusX <= 1 && touch.radiusY <= 1 } function farAway(touch, other) { if (other.left == null) { return true } var dx = other.left - touch.left, dy = other.top - touch.top; return dx * dx + dy * dy > 20 * 20 } on(d.scroller, "touchstart", function (e) { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { d.input.ensurePolled(); clearTimeout(touchFinished); var now = +new Date; d.activeTouch = {start: now, moved: false, prev: now - prevTouch.end <= 300 ? prevTouch : null}; if (e.touches.length == 1) { d.activeTouch.left = e.touches[0].pageX; d.activeTouch.top = e.touches[0].pageY; } } }); on(d.scroller, "touchmove", function () { if (d.activeTouch) { d.activeTouch.moved = true; } }); on(d.scroller, "touchend", function (e) { var touch = d.activeTouch; if (touch && !eventInWidget(d, e) && touch.left != null && !touch.moved && new Date - touch.start < 300) { var pos = cm.coordsChar(d.activeTouch, "page"), range; if (!touch.prev || farAway(touch, touch.prev)) // Single tap { range = new Range(pos, pos); } else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap { range = cm.findWordAt(pos); } else // Triple tap { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } cm.setSelection(range.anchor, range.head); cm.focus(); e_preventDefault(e); } finishTouch(); }); on(d.scroller, "touchcancel", finishTouch); // Sync scrolling between fake scrollbars and real scrollable // area, ensure viewport is updated when scrolling. on(d.scroller, "scroll", function () { if (d.scroller.clientHeight) { updateScrollTop(cm, d.scroller.scrollTop); setScrollLeft(cm, d.scroller.scrollLeft, true); signal(cm, "scroll", cm); } }); // Listen to wheel events in order to try and update the viewport on time. on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); d.dragFunctions = { enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, start: function (e) { return onDragStart(cm, e); }, drop: operation(cm, onDrop), leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} }; var inp = d.input.getField(); on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); on(inp, "keydown", operation(cm, onKeyDown)); on(inp, "keypress", operation(cm, onKeyPress)); on(inp, "focus", function (e) { return onFocus(cm, e); }); on(inp, "blur", function (e) { return onBlur(cm, e); }); } var initHooks = []; CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; // Indent the given line. The how parameter can be "smart", // "add"/null, "subtract", or "prev". When aggressive is false // (typically set to true for forced single-line indents), empty // lines are not indented, and places where the mode returns Pass // are left alone. function indentLine(cm, n, how, aggressive) { var doc = cm.doc, state; if (how == null) { how = "add"; } if (how == "smart") { // Fall back to "prev" when the mode doesn't have an indentation // method. if (!doc.mode.indent) { how = "prev"; } else { state = getContextBefore(cm, n).state; } } var tabSize = cm.options.tabSize; var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); if (line.stateAfter) { line.stateAfter = null; } var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (!aggressive && !/\S/.test(line.text)) { indentation = 0; how = "not"; } else if (how == "smart") { indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass || indentation > 150) { if (!aggressive) { return } how = "prev"; } } if (how == "prev") { if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } else { indentation = 0; } } else if (how == "add") { indentation = curSpace + cm.options.indentUnit; } else if (how == "subtract") { indentation = curSpace - cm.options.indentUnit; } else if (typeof how == "number") { indentation = curSpace + how; } indentation = Math.max(0, indentation); var indentString = "", pos = 0; if (cm.options.indentWithTabs) { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } if (pos < indentation) { indentString += spaceStr(indentation - pos); } if (indentString != curSpaceString) { replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; return true } else { // Ensure that, if the cursor was in the whitespace at the start // of the line, it is moved to the end of that space. for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { var range = doc.sel.ranges[i$1]; if (range.head.line == n && range.head.ch < curSpaceString.length) { var pos$1 = Pos(n, curSpaceString.length); replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); break } } } } // This will be set to a {lineWise: bool, text: [string]} object, so // that, when pasting, we know what kind of selections the copied // text was made out of. var lastCopied = null; function setLastCopied(newLastCopied) { lastCopied = newLastCopied; } function applyTextInput(cm, inserted, deleted, sel, origin) { var doc = cm.doc; cm.display.shift = false; if (!sel) { sel = doc.sel; } var recent = +new Date - 200; var paste = origin == "paste" || cm.state.pasteIncoming > recent; var textLines = splitLinesAuto(inserted), multiPaste = null; // When pasting N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = []; for (var i = 0; i < lastCopied.text.length; i++) { multiPaste.push(doc.splitLines(lastCopied.text[i])); } } } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { multiPaste = map(textLines, function (l) { return [l]; }); } } var updateInput = cm.curOp.updateInput; // Normal behavior is to insert the new text into every selection for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { var range$$1 = sel.ranges[i$1]; var from = range$$1.from(), to = range$$1.to(); if (range$$1.empty()) { if (deleted && deleted > 0) // Handle deletion { from = Pos(from.line, from.ch - deleted); } else if (cm.state.overwrite && !paste) // Handle overwrite { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) { from = to = Pos(from.line, 0); } } var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; makeChange(cm.doc, changeEvent); signalLater(cm, "inputRead", cm, changeEvent); } if (inserted && !paste) { triggerElectric(cm, inserted); } ensureCursorVisible(cm); if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } cm.curOp.typing = true; cm.state.pasteIncoming = cm.state.cutIncoming = -1; } function handlePaste(e, cm) { var pasted = e.clipboardData && e.clipboardData.getData("Text"); if (pasted) { e.preventDefault(); if (!cm.isReadOnly() && !cm.options.disableInput) { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } return true } } function triggerElectric(cm, inserted) { // When an 'electric' character is inserted, immediately trigger a reindent if (!cm.options.electricChars || !cm.options.smartIndent) { return } var sel = cm.doc.sel; for (var i = sel.ranges.length - 1; i >= 0; i--) { var range$$1 = sel.ranges[i]; if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue } var mode = cm.getModeAt(range$$1.head); var indented = false; if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { indented = indentLine(cm, range$$1.head.line, "smart"); break } } } else if (mode.electricInput) { if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch))) { indented = indentLine(cm, range$$1.head.line, "smart"); } } if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); } } } function copyableRanges(cm) { var text = [], ranges = []; for (var i = 0; i < cm.doc.sel.ranges.length; i++) { var line = cm.doc.sel.ranges[i].head.line; var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; ranges.push(lineRange); text.push(cm.getRange(lineRange.anchor, lineRange.head)); } return {text: text, ranges: ranges} } function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { field.setAttribute("autocorrect", autocorrect ? "" : "off"); field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); field.setAttribute("spellcheck", !!spellcheck); } function hiddenTextarea() { var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); // The textarea is kept positioned near the cursor to prevent the // fact that it'll be scrolled into view on input from scrolling // our fake cursor out of view. On webkit, when wrap=off, paste is // very slow. So make the area wide instead. if (webkit) { te.style.width = "1000px"; } else { te.setAttribute("wrap", "off"); } // If border: 0; -- iOS fails to open keyboard (issue #1287) if (ios) { te.style.border = "1px solid black"; } disableBrowserMagic(te); return div } // The publicly visible API. Note that methodOp(f) means // 'wrap f in an operation, performed on its `this` parameter'. // This is not the complete set of editor methods. Most of the // methods defined on the Doc type are also injected into // CodeMirror.prototype, for backwards compatibility and // convenience. function addEditorMethods(CodeMirror) { var optionHandlers = CodeMirror.optionHandlers; var helpers = CodeMirror.helpers = {}; CodeMirror.prototype = { constructor: CodeMirror, focus: function(){window.focus(); this.display.input.focus();}, setOption: function(option, value) { var options = this.options, old = options[option]; if (options[option] == value && option != "mode") { return } options[option] = value; if (optionHandlers.hasOwnProperty(option)) { operation(this, optionHandlers[option])(this, value, old); } signal(this, "optionChange", this, option); }, getOption: function(option) {return this.options[option]}, getDoc: function() {return this.doc}, addKeyMap: function(map$$1, bottom) { this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1)); }, removeKeyMap: function(map$$1) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) { if (maps[i] == map$$1 || maps[i].name == map$$1) { maps.splice(i, 1); return true } } }, addOverlay: methodOp(function(spec, options) { var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); if (mode.startState) { throw new Error("Overlays may not be stateful.") } insertSorted(this.state.overlays, {mode: mode, modeSpec: spec, opaque: options && options.opaque, priority: (options && options.priority) || 0}, function (overlay) { return overlay.priority; }); this.state.modeGen++; regChange(this); }), removeOverlay: methodOp(function(spec) { var this$1 = this; var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { var cur = overlays[i].modeSpec; if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); this$1.state.modeGen++; regChange(this$1); return } } }), indentLine: methodOp(function(n, dir, aggressive) { if (typeof dir != "string" && typeof dir != "number") { if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } else { dir = dir ? "add" : "subtract"; } } if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } }), indentSelection: methodOp(function(how) { var this$1 = this; var ranges = this.doc.sel.ranges, end = -1; for (var i = 0; i < ranges.length; i++) { var range$$1 = ranges[i]; if (!range$$1.empty()) { var from = range$$1.from(), to = range$$1.to(); var start = Math.max(end, from.line); end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; for (var j = start; j < end; ++j) { indentLine(this$1, j, how); } var newRanges = this$1.doc.sel.ranges; if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } } else if (range$$1.head.line > end) { indentLine(this$1, range$$1.head.line, how, true); end = range$$1.head.line; if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); } } } }), // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(pos, precise) { return takeToken(this, pos, precise) }, getLineTokens: function(line, precise) { return takeToken(this, Pos(line), precise, true) }, getTokenTypeAt: function(pos) { pos = clipPos(this.doc, pos); var styles = getLineStyles(this, getLine(this.doc, pos.line)); var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; var type; if (ch == 0) { type = styles[2]; } else { for (;;) { var mid = (before + after) >> 1; if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } else { type = styles[mid * 2 + 2]; break } } } var cut = type ? type.indexOf("overlay ") : -1; return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) }, getModeAt: function(pos) { var mode = this.doc.mode; if (!mode.innerMode) { return mode } return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode }, getHelper: function(pos, type) { return this.getHelpers(pos, type)[0] }, getHelpers: function(pos, type) { var this$1 = this; var found = []; if (!helpers.hasOwnProperty(type)) { return found } var help = helpers[type], mode = this.getModeAt(pos); if (typeof mode[type] == "string") { if (help[mode[type]]) { found.push(help[mode[type]]); } } else if (mode[type]) { for (var i = 0; i < mode[type].length; i++) { var val = help[mode[type][i]]; if (val) { found.push(val); } } } else if (mode.helperType && help[mode.helperType]) { found.push(help[mode.helperType]); } else if (help[mode.name]) { found.push(help[mode.name]); } for (var i$1 = 0; i$1 < help._global.length; i$1++) { var cur = help._global[i$1]; if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1) { found.push(cur.val); } } return found }, getStateAfter: function(line, precise) { var doc = this.doc; line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); return getContextBefore(this, line + 1, precise).state }, cursorCoords: function(start, mode) { var pos, range$$1 = this.doc.sel.primary(); if (start == null) { pos = range$$1.head; } else if (typeof start == "object") { pos = clipPos(this.doc, start); } else { pos = start ? range$$1.from() : range$$1.to(); } return cursorCoords(this, pos, mode || "page") }, charCoords: function(pos, mode) { return charCoords(this, clipPos(this.doc, pos), mode || "page") }, coordsChar: function(coords, mode) { coords = fromCoordSystem(this, coords, mode || "page"); return coordsChar(this, coords.left, coords.top) }, lineAtHeight: function(height, mode) { height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; return lineAtHeight(this.doc, height + this.display.viewOffset) }, heightAtLine: function(line, mode, includeWidgets) { var end = false, lineObj; if (typeof line == "number") { var last = this.doc.first + this.doc.size - 1; if (line < this.doc.first) { line = this.doc.first; } else if (line > last) { line = last; end = true; } lineObj = getLine(this.doc, line); } else { lineObj = line; } return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + (end ? this.doc.height - heightAtLine(lineObj) : 0) }, defaultTextHeight: function() { return textHeight(this.display) }, defaultCharWidth: function() { return charWidth(this.display) }, getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, addWidget: function(pos, node, scroll, vert, horiz) { var display = this.display; pos = cursorCoords(this, clipPos(this.doc, pos)); var top = pos.bottom, left = pos.left; node.style.position = "absolute"; node.setAttribute("cm-ignore-events", "true"); this.display.input.setUneditable(node); display.sizer.appendChild(node); if (vert == "over") { top = pos.top; } else if (vert == "above" || vert == "near") { var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); // Default to positioning above (if specified and possible); otherwise default to positioning below if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) { top = pos.top - node.offsetHeight; } else if (pos.bottom + node.offsetHeight <= vspace) { top = pos.bottom; } if (left + node.offsetWidth > hspace) { left = hspace - node.offsetWidth; } } node.style.top = top + "px"; node.style.left = node.style.right = ""; if (horiz == "right") { left = display.sizer.clientWidth - node.offsetWidth; node.style.right = "0px"; } else { if (horiz == "left") { left = 0; } else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } node.style.left = left + "px"; } if (scroll) { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } }, triggerOnKeyDown: methodOp(onKeyDown), triggerOnKeyPress: methodOp(onKeyPress), triggerOnKeyUp: onKeyUp, triggerOnMouseDown: methodOp(onMouseDown), execCommand: function(cmd) { if (commands.hasOwnProperty(cmd)) { return commands[cmd].call(null, this) } }, triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), findPosH: function(from, amount, unit, visually) { var this$1 = this; var dir = 1; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { cur = findPosH(this$1.doc, cur, dir, unit, visually); if (cur.hitSide) { break } } return cur }, moveH: methodOp(function(dir, unit) { var this$1 = this; this.extendSelectionsBy(function (range$$1) { if (this$1.display.shift || this$1.doc.extend || range$$1.empty()) { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) } else { return dir < 0 ? range$$1.from() : range$$1.to() } }, sel_move); }), deleteH: methodOp(function(dir, unit) { var sel = this.doc.sel, doc = this.doc; if (sel.somethingSelected()) { doc.replaceSelection("", null, "+delete"); } else { deleteNearSelection(this, function (range$$1) { var other = findPosH(doc, range$$1.head, dir, unit, false); return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other} }); } }), findPosV: function(from, amount, unit, goalColumn) { var this$1 = this; var dir = 1, x = goalColumn; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { var coords = cursorCoords(this$1, cur, "div"); if (x == null) { x = coords.left; } else { coords.left = x; } cur = findPosV(this$1, coords, dir, unit); if (cur.hitSide) { break } } return cur }, moveV: methodOp(function(dir, unit) { var this$1 = this; var doc = this.doc, goals = []; var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); doc.extendSelectionsBy(function (range$$1) { if (collapse) { return dir < 0 ? range$$1.from() : range$$1.to() } var headPos = cursorCoords(this$1, range$$1.head, "div"); if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; } goals.push(headPos.left); var pos = findPosV(this$1, headPos, dir, unit); if (unit == "page" && range$$1 == doc.sel.primary()) { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } return pos }, sel_move); if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) { doc.sel.ranges[i].goalColumn = goals[i]; } } }), // Find the word at the given position (as returned by coordsChar). findWordAt: function(pos) { var doc = this.doc, line = getLine(doc, pos.line).text; var start = pos.ch, end = pos.ch; if (line) { var helper = this.getHelper(pos, "wordChars"); if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } var startChar = line.charAt(start); var check = isWordChar(startChar, helper) ? function (ch) { return isWordChar(ch, helper); } : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; while (start > 0 && check(line.charAt(start - 1))) { --start; } while (end < line.length && check(line.charAt(end))) { ++end; } } return new Range(Pos(pos.line, start), Pos(pos.line, end)) }, toggleOverwrite: function(value) { if (value != null && value == this.state.overwrite) { return } if (this.state.overwrite = !this.state.overwrite) { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } else { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } signal(this, "overwriteToggle", this, this.state.overwrite); }, hasFocus: function() { return this.display.input.getField() == activeElt() }, isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), getScrollInfo: function() { var scroller = this.display.scroller; return {left: scroller.scrollLeft, top: scroller.scrollTop, height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, clientHeight: displayHeight(this), clientWidth: displayWidth(this)} }, scrollIntoView: methodOp(function(range$$1, margin) { if (range$$1 == null) { range$$1 = {from: this.doc.sel.primary().head, to: null}; if (margin == null) { margin = this.options.cursorScrollMargin; } } else if (typeof range$$1 == "number") { range$$1 = {from: Pos(range$$1, 0), to: null}; } else if (range$$1.from == null) { range$$1 = {from: range$$1, to: null}; } if (!range$$1.to) { range$$1.to = range$$1.from; } range$$1.margin = margin || 0; if (range$$1.from.line != null) { scrollToRange(this, range$$1); } else { scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin); } }), setSize: methodOp(function(width, height) { var this$1 = this; var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; if (width != null) { this.display.wrapper.style.width = interpret(width); } if (height != null) { this.display.wrapper.style.height = interpret(height); } if (this.options.lineWrapping) { clearLineMeasurementCache(this); } var lineNo$$1 = this.display.viewFrom; this.doc.iter(lineNo$$1, this.display.viewTo, function (line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } } ++lineNo$$1; }); this.curOp.forceUpdate = true; signal(this, "refresh", this); }), operation: function(f){return runInOp(this, f)}, startOperation: function(){return startOperation(this)}, endOperation: function(){return endOperation(this)}, refresh: methodOp(function() { var oldHeight = this.display.cachedTextHeight; regChange(this); this.curOp.forceUpdate = true; clearCaches(this); scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); updateGutterSpace(this.display); if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) { estimateLineHeights(this); } signal(this, "refresh", this); }), swapDoc: methodOp(function(doc) { var old = this.doc; old.cm = null; // Cancel the current text selection if any (#5821) if (this.state.selectingText) { this.state.selectingText(); } attachDoc(this, doc); clearCaches(this); this.display.input.reset(); scrollToCoords(this, doc.scrollLeft, doc.scrollTop); this.curOp.forceScroll = true; signalLater(this, "swapDoc", this, old); return old }), phrase: function(phraseText) { var phrases = this.options.phrases; return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText }, getInputField: function(){return this.display.input.getField()}, getWrapperElement: function(){return this.display.wrapper}, getScrollerElement: function(){return this.display.scroller}, getGutterElement: function(){return this.display.gutters} }; eventMixin(CodeMirror); CodeMirror.registerHelper = function(type, name, value) { if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } helpers[type][name] = value; }; CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { CodeMirror.registerHelper(type, name, value); helpers[type]._global.push({pred: predicate, val: value}); }; } // Used for horizontal relative motion. Dir is -1 or 1 (left or // right), unit can be "char", "column" (like char, but doesn't // cross line boundaries), "word" (across next word), or "group" (to // the start of next group of word or non-word-non-whitespace // chars). The visually param controls whether, in right-to-left // text, direction 1 means to move towards the next index in the // string, or towards the character to the right of the current // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { var oldPos = pos; var origDir = dir; var lineObj = getLine(doc, pos.line); function findNextLine() { var l = pos.line + dir; if (l < doc.first || l >= doc.first + doc.size) { return false } pos = new Pos(l, pos.ch, pos.sticky); return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { var next; if (visually) { next = moveVisually(doc.cm, lineObj, pos, dir); } else { next = moveLogically(lineObj, pos, dir); } if (next == null) { if (!boundToLine && findNextLine()) { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); } else { return false } } else { pos = next; } return true } if (unit == "char") { moveOnce(); } else if (unit == "column") { moveOnce(true); } else if (unit == "word" || unit == "group") { var sawType = null, group = unit == "group"; var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); for (var first = true;; first = false) { if (dir < 0 && !moveOnce(!first)) { break } var cur = lineObj.text.charAt(pos.ch) || "\n"; var type = isWordChar(cur, helper) ? "w" : group && cur == "\n" ? "n" : !group || /\s/.test(cur) ? null : "p"; if (group && !first && !type) { type = "s"; } if (sawType && sawType != type) { if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} break } if (type) { sawType = type; } if (dir > 0 && !moveOnce(!first)) { break } } } var result = skipAtomic(doc, pos, oldPos, origDir, true); if (equalCursorPos(oldPos, result)) { result.hitSide = true; } return result } // For relative vertical movement. Dir may be -1 or 1. Unit can be // "page" or "line". The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosV(cm, pos, dir, unit) { var doc = cm.doc, x = pos.left, y; if (unit == "page") { var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } var target; for (;;) { target = coordsChar(cm, x, y); if (!target.outside) { break } if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } y += dir * 5; } return target } // CONTENTEDITABLE INPUT STYLE var ContentEditableInput = function(cm) { this.cm = cm; this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; this.polling = new Delayed(); this.composing = null; this.gracePeriod = false; this.readDOMTimeout = null; }; ContentEditableInput.prototype.init = function (display) { var this$1 = this; var input = this, cm = input.cm; var div = input.div = display.lineDiv; disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); on(div, "paste", function (e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } // IE doesn't fire input events, so we schedule a read for the pasted content in this way if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } }); on(div, "compositionstart", function (e) { this$1.composing = {data: e.data, done: false}; }); on(div, "compositionupdate", function (e) { if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } }); on(div, "compositionend", function (e) { if (this$1.composing) { if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } this$1.composing.done = true; } }); on(div, "touchstart", function () { return input.forceCompositionEnd(); }); on(div, "input", function () { if (!this$1.composing) { this$1.readFromDOMSoon(); } }); function onCopyCut(e) { if (signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } } else if (!cm.options.lineWiseCopyCut) { return } else { var ranges = copyableRanges(cm); setLastCopied({lineWise: true, text: ranges.text}); if (e.type == "cut") { cm.operation(function () { cm.setSelections(ranges.ranges, 0, sel_dontScroll); cm.replaceSelection("", null, "cut"); }); } } if (e.clipboardData) { e.clipboardData.clearData(); var content = lastCopied.text.join("\n"); // iOS exposes the clipboard API, but seems to discard content inserted into it e.clipboardData.setData("Text", content); if (e.clipboardData.getData("Text") == content) { e.preventDefault(); return } } // Old-fashioned briefly-focus-a-textarea hack var kludge = hiddenTextarea(), te = kludge.firstChild; cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); te.value = lastCopied.text.join("\n"); var hadFocus = document.activeElement; selectInput(te); setTimeout(function () { cm.display.lineSpace.removeChild(kludge); hadFocus.focus(); if (hadFocus == div) { input.showPrimarySelection(); } }, 50); } on(div, "copy", onCopyCut); on(div, "cut", onCopyCut); }; ContentEditableInput.prototype.prepareSelection = function () { var result = prepareSelection(this.cm, false); result.focus = this.cm.state.focused; return result }; ContentEditableInput.prototype.showSelection = function (info, takeFocus) { if (!info || !this.cm.display.view.length) { return } if (info.focus || takeFocus) { this.showPrimarySelection(); } this.showMultipleSelections(info); }; ContentEditableInput.prototype.getSelection = function () { return this.cm.display.wrapper.ownerDocument.getSelection() }; ContentEditableInput.prototype.showPrimarySelection = function () { var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); var from = prim.from(), to = prim.to(); if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { sel.removeAllRanges(); return } var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && cmp(minPos(curAnchor, curFocus), from) == 0 && cmp(maxPos(curAnchor, curFocus), to) == 0) { return } var view = cm.display.view; var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || {node: view[0].measure.map[2], offset: 0}; var end = to.line < cm.display.viewTo && posToDOM(cm, to); if (!end) { var measure = view[view.length - 1].measure; var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}; } if (!start || !end) { sel.removeAllRanges(); return } var old = sel.rangeCount && sel.getRangeAt(0), rng; try { rng = range(start.node, start.offset, end.offset, end.node); } catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible if (rng) { if (!gecko && cm.state.focused) { sel.collapse(start.node, start.offset); if (!rng.collapsed) { sel.removeAllRanges(); sel.addRange(rng); } } else { sel.removeAllRanges(); sel.addRange(rng); } if (old && sel.anchorNode == null) { sel.addRange(old); } else if (gecko) { this.startGracePeriod(); } } this.rememberSelection(); }; ContentEditableInput.prototype.startGracePeriod = function () { var this$1 = this; clearTimeout(this.gracePeriod); this.gracePeriod = setTimeout(function () { this$1.gracePeriod = false; if (this$1.selectionChanged()) { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } }, 20); }; ContentEditableInput.prototype.showMultipleSelections = function (info) { removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); }; ContentEditableInput.prototype.rememberSelection = function () { var sel = this.getSelection(); this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; }; ContentEditableInput.prototype.selectionInEditor = function () { var sel = this.getSelection(); if (!sel.rangeCount) { return false } var node = sel.getRangeAt(0).commonAncestorContainer; return contains(this.div, node) }; ContentEditableInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor") { if (!this.selectionInEditor()) { this.showSelection(this.prepareSelection(), true); } this.div.focus(); } }; ContentEditableInput.prototype.blur = function () { this.div.blur(); }; ContentEditableInput.prototype.getField = function () { return this.div }; ContentEditableInput.prototype.supportsTouch = function () { return true }; ContentEditableInput.prototype.receivedFocus = function () { var input = this; if (this.selectionInEditor()) { this.pollSelection(); } else { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } function poll() { if (input.cm.state.focused) { input.pollSelection(); input.polling.set(input.cm.options.pollInterval, poll); } } this.polling.set(this.cm.options.pollInterval, poll); }; ContentEditableInput.prototype.selectionChanged = function () { var sel = this.getSelection(); return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset }; ContentEditableInput.prototype.pollSelection = function () { if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } var sel = this.getSelection(), cm = this.cm; // On Android Chrome (version 56, at least), backspacing into an // uneditable block element will put the cursor in that element, // and then, because it's not editable, hide the virtual keyboard. // Because Android doesn't allow us to actually detect backspace // presses in a sane way, this code checks for when that happens // and simulates a backspace press in this case. if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); this.blur(); this.focus(); return } if (this.composing) { return } this.rememberSelection(); var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var head = domToPos(cm, sel.focusNode, sel.focusOffset); if (anchor && head) { runInOp(cm, function () { setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } }); } }; ContentEditableInput.prototype.pollContent = function () { if (this.readDOMTimeout != null) { clearTimeout(this.readDOMTimeout); this.readDOMTimeout = null; } var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); var from = sel.from(), to = sel.to(); if (from.ch == 0 && from.line > cm.firstLine()) { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) { to = Pos(to.line + 1, 0); } if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } var fromIndex, fromLine, fromNode; if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { fromLine = lineNo(display.view[0].line); fromNode = display.view[0].node; } else { fromLine = lineNo(display.view[fromIndex].line); fromNode = display.view[fromIndex - 1].node.nextSibling; } var toIndex = findViewIndex(cm, to.line); var toLine, toNode; if (toIndex == display.view.length - 1) { toLine = display.viewTo - 1; toNode = display.lineDiv.lastChild; } else { toLine = lineNo(display.view[toIndex + 1].line) - 1; toNode = display.view[toIndex + 1].node.previousSibling; } if (!fromNode) { return false } var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); while (newText.length > 1 && oldText.length > 1) { if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } else { break } } var cutFront = 0, cutEnd = 0; var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) { ++cutFront; } var newBot = lst(newText), oldBot = lst(oldText); var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), oldBot.length - (oldText.length == 1 ? cutFront : 0)); while (cutEnd < maxCutEnd && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { ++cutEnd; } // Try to move start of change to start of selection if ambiguous if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { while (cutFront && cutFront > from.ch && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { cutFront--; cutEnd++; } } newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); var chFrom = Pos(fromLine, cutFront); var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { replaceRange(cm.doc, newText, chFrom, chTo, "+input"); return true } }; ContentEditableInput.prototype.ensurePolled = function () { this.forceCompositionEnd(); }; ContentEditableInput.prototype.reset = function () { this.forceCompositionEnd(); }; ContentEditableInput.prototype.forceCompositionEnd = function () { if (!this.composing) { return } clearTimeout(this.readDOMTimeout); this.composing = null; this.updateFromDOM(); this.div.blur(); this.div.focus(); }; ContentEditableInput.prototype.readFromDOMSoon = function () { var this$1 = this; if (this.readDOMTimeout != null) { return } this.readDOMTimeout = setTimeout(function () { this$1.readDOMTimeout = null; if (this$1.composing) { if (this$1.composing.done) { this$1.composing = null; } else { return } } this$1.updateFromDOM(); }, 80); }; ContentEditableInput.prototype.updateFromDOM = function () { var this$1 = this; if (this.cm.isReadOnly() || !this.pollContent()) { runInOp(this.cm, function () { return regChange(this$1.cm); }); } }; ContentEditableInput.prototype.setUneditable = function (node) { node.contentEditable = "false"; }; ContentEditableInput.prototype.onKeyPress = function (e) { if (e.charCode == 0 || this.composing) { return } e.preventDefault(); if (!this.cm.isReadOnly()) { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } }; ContentEditableInput.prototype.readOnlyChanged = function (val) { this.div.contentEditable = String(val != "nocursor"); }; ContentEditableInput.prototype.onContextMenu = function () {}; ContentEditableInput.prototype.resetPosition = function () {}; ContentEditableInput.prototype.needsContentAttribute = true; function posToDOM(cm, pos) { var view = findViewForLine(cm, pos.line); if (!view || view.hidden) { return null } var line = getLine(cm.doc, pos.line); var info = mapFromLineView(view, line, pos.line); var order = getOrder(line, cm.doc.direction), side = "left"; if (order) { var partPos = getBidiPartAt(order, pos.ch); side = partPos % 2 ? "right" : "left"; } var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); result.offset = result.collapse == "right" ? result.end : result.start; return result } function isInGutter(node) { for (var scan = node; scan; scan = scan.parentNode) { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } return false } function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } function domTextBetween(cm, from, to, fromLine, toLine) { var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; function recognizeMarker(id) { return function (marker) { return marker.id == id; } } function close() { if (closing) { text += lineSep; if (extraLinebreak) { text += lineSep; } closing = extraLinebreak = false; } } function addText(str) { if (str) { close(); text += str; } } function walk(node) { if (node.nodeType == 1) { var cmText = node.getAttribute("cm-text"); if (cmText) { addText(cmText); return } var markerID = node.getAttribute("cm-marker"), range$$1; if (markerID) { var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); if (found.length && (range$$1 = found[0].find(0))) { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); } return } if (node.getAttribute("contenteditable") == "false") { return } var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } if (isBlock) { close(); } for (var i = 0; i < node.childNodes.length; i++) { walk(node.childNodes[i]); } if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } if (isBlock) { closing = true; } } else if (node.nodeType == 3) { addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); } } for (;;) { walk(from); if (from == to) { break } from = from.nextSibling; extraLinebreak = false; } return text } function domToPos(cm, node, offset) { var lineNode; if (node == cm.display.lineDiv) { lineNode = cm.display.lineDiv.childNodes[offset]; if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } node = null; offset = 0; } else { for (lineNode = node;; lineNode = lineNode.parentNode) { if (!lineNode || lineNode == cm.display.lineDiv) { return null } if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } } } for (var i = 0; i < cm.display.view.length; i++) { var lineView = cm.display.view[i]; if (lineView.node == lineNode) { return locateNodeInLineView(lineView, node, offset) } } } function locateNodeInLineView(lineView, node, offset) { var wrapper = lineView.text.firstChild, bad = false; if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } if (node == wrapper) { bad = true; node = wrapper.childNodes[offset]; offset = 0; if (!node) { var line = lineView.rest ? lst(lineView.rest) : lineView.line; return badPos(Pos(lineNo(line), line.text.length), bad) } } var textNode = node.nodeType == 3 ? node : null, topNode = node; if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { textNode = node.firstChild; if (offset) { offset = textNode.nodeValue.length; } } while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } var measure = lineView.measure, maps = measure.maps; function find(textNode, topNode, offset) { for (var i = -1; i < (maps ? maps.length : 0); i++) { var map$$1 = i < 0 ? measure.map : maps[i]; for (var j = 0; j < map$$1.length; j += 3) { var curNode = map$$1[j + 2]; if (curNode == textNode || curNode == topNode) { var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); var ch = map$$1[j] + offset; if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; } return Pos(line, ch) } } } } var found = find(textNode, topNode, offset); if (found) { return badPos(found, bad) } // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { found = find(after, after.firstChild, 0); if (found) { return badPos(Pos(found.line, found.ch - dist), bad) } else { dist += after.textContent.length; } } for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { found = find(before, before.firstChild, -1); if (found) { return badPos(Pos(found.line, found.ch + dist$1), bad) } else { dist$1 += before.textContent.length; } } } // TEXTAREA INPUT STYLE var TextareaInput = function(cm) { this.cm = cm; // See input.poll and input.reset this.prevInput = ""; // Flag that indicates whether we expect input to appear real soon // now (after some event like 'keypress' or 'input') and are // polling intensively. this.pollingFast = false; // Self-resetting timeout for the poller this.polling = new Delayed(); // Used to work around IE issue with selection being forgotten when focus moves away from textarea this.hasSelection = false; this.composing = null; }; TextareaInput.prototype.init = function (display) { var this$1 = this; var input = this, cm = this.cm; this.createField(display); var te = this.textarea; display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) if (ios) { te.style.width = "0px"; } on(te, "input", function () { if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } input.poll(); }); on(te, "paste", function (e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } cm.state.pasteIncoming = +new Date; input.fastPoll(); }); function prepareCopyCut(e) { if (signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); } else if (!cm.options.lineWiseCopyCut) { return } else { var ranges = copyableRanges(cm); setLastCopied({lineWise: true, text: ranges.text}); if (e.type == "cut") { cm.setSelections(ranges.ranges, null, sel_dontScroll); } else { input.prevInput = ""; te.value = ranges.text.join("\n"); selectInput(te); } } if (e.type == "cut") { cm.state.cutIncoming = +new Date; } } on(te, "cut", prepareCopyCut); on(te, "copy", prepareCopyCut); on(display.scroller, "paste", function (e) { if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } if (!te.dispatchEvent) { cm.state.pasteIncoming = +new Date; input.focus(); return } // Pass the `paste` event to the textarea so it's handled by its event listener. var event = new Event("paste"); event.clipboardData = e.clipboardData; te.dispatchEvent(event); }); // Prevent normal selection in the editor (we handle our own) on(display.lineSpace, "selectstart", function (e) { if (!eventInWidget(display, e)) { e_preventDefault(e); } }); on(te, "compositionstart", function () { var start = cm.getCursor("from"); if (input.composing) { input.composing.range.clear(); } input.composing = { start: start, range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) }; }); on(te, "compositionend", function () { if (input.composing) { input.poll(); input.composing.range.clear(); input.composing = null; } }); }; TextareaInput.prototype.createField = function (_display) { // Wraps and hides input textarea this.wrapper = hiddenTextarea(); // The semihidden textarea that is focused when the editor is // focused, and receives input. this.textarea = this.wrapper.firstChild; }; TextareaInput.prototype.prepareSelection = function () { // Redraw the selection and/or cursor var cm = this.cm, display = cm.display, doc = cm.doc; var result = prepareSelection(cm); // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)); result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)); } return result }; TextareaInput.prototype.showSelection = function (drawn) { var cm = this.cm, display = cm.display; removeChildrenAndAdd(display.cursorDiv, drawn.cursors); removeChildrenAndAdd(display.selectionDiv, drawn.selection); if (drawn.teTop != null) { this.wrapper.style.top = drawn.teTop + "px"; this.wrapper.style.left = drawn.teLeft + "px"; } }; // Reset the input to correspond to the selection (or to be empty, // when not typing and nothing is selected) TextareaInput.prototype.reset = function (typing) { if (this.contextMenuPending || this.composing) { return } var cm = this.cm; if (cm.somethingSelected()) { this.prevInput = ""; var content = cm.getSelection(); this.textarea.value = content; if (cm.state.focused) { selectInput(this.textarea); } if (ie && ie_version >= 9) { this.hasSelection = content; } } else if (!typing) { this.prevInput = this.textarea.value = ""; if (ie && ie_version >= 9) { this.hasSelection = null; } } }; TextareaInput.prototype.getField = function () { return this.textarea }; TextareaInput.prototype.supportsTouch = function () { return false }; TextareaInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { try { this.textarea.focus(); } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM } }; TextareaInput.prototype.blur = function () { this.textarea.blur(); }; TextareaInput.prototype.resetPosition = function () { this.wrapper.style.top = this.wrapper.style.left = 0; }; TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. TextareaInput.prototype.slowPoll = function () { var this$1 = this; if (this.pollingFast) { return } this.polling.set(this.cm.options.pollInterval, function () { this$1.poll(); if (this$1.cm.state.focused) { this$1.slowPoll(); } }); }; // When an event has just come in that is likely to add or change // something in the input textarea, we poll faster, to ensure that // the change appears on the screen quickly. TextareaInput.prototype.fastPoll = function () { var missed = false, input = this; input.pollingFast = true; function p() { var changed = input.poll(); if (!changed && !missed) {missed = true; input.polling.set(60, p);} else {input.pollingFast = false; input.slowPoll();} } input.polling.set(20, p); }; // Read input from the textarea, and update the document to match. // When something is selected, it is present in the textarea, and // selected (unless it is huge, in which case a placeholder is // used). When nothing is selected, the cursor sits after previously // seen text (can be empty), which is stored in prevInput (we must // not reset the textarea when typing, because that breaks IME). TextareaInput.prototype.poll = function () { var this$1 = this; var cm = this.cm, input = this.textarea, prevInput = this.prevInput; // Since this is called a *lot*, try to bail out as cheaply as // possible when it is clear that nothing happened. hasSelection // will be the case when there is a lot of text in the textarea, // in which case reading its value would be expensive. if (this.contextMenuPending || !cm.state.focused || (hasSelection(input) && !prevInput && !this.composing) || cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) { return false } var text = input.value; // If nothing changed, bail. if (text == prevInput && !cm.somethingSelected()) { return false } // Work around nonsensical selection resetting in IE9/10, and // inexplicable appearance of private area unicode characters on // some key combos in Mac (#2689). if (ie && ie_version >= 9 && this.hasSelection === text || mac && /[\uf700-\uf7ff]/.test(text)) { cm.display.input.reset(); return false } if (cm.doc.sel == cm.display.selForContextMenu) { var first = text.charCodeAt(0); if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } } // Find the part of the input that is actually new var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } runInOp(cm, function () { applyTextInput(cm, text.slice(same), prevInput.length - same, null, this$1.composing ? "*compose" : null); // Don't leave long text in the textarea, since it makes further polling slow if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } else { this$1.prevInput = text; } if (this$1.composing) { this$1.composing.range.clear(); this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), {className: "CodeMirror-composing"}); } }); return true }; TextareaInput.prototype.ensurePolled = function () { if (this.pollingFast && this.poll()) { this.pollingFast = false; } }; TextareaInput.prototype.onKeyPress = function () { if (ie && ie_version >= 9) { this.hasSelection = null; } this.fastPoll(); }; TextareaInput.prototype.onContextMenu = function (e) { var input = this, cm = input.cm, display = cm.display, te = input.textarea; if (input.contextMenuPending) { input.contextMenuPending(); } var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; if (!pos || presto) { return } // Opera is difficult. // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. var reset = cm.options.resetSelectionOnContextMenu; if (reset && cm.doc.sel.contains(pos) == -1) { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); input.wrapper.style.cssText = "position: static"; te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; var oldScrollY; if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) display.input.focus(); if (webkit) { window.scrollTo(null, oldScrollY); } display.input.reset(); // Adds "Select all" to context menu in FF if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } input.contextMenuPending = rehide; display.selForContextMenu = cm.doc.sel; clearTimeout(display.detectingSelectAll); // Select-all will be greyed out if there's nothing to select, so // this adds a zero-width space so that we can later check whether // it got selected. function prepareSelectAllHack() { if (te.selectionStart != null) { var selected = cm.somethingSelected(); var extval = "\u200b" + (selected ? te.value : ""); te.value = "\u21da"; // Used to catch context-menu undo te.value = extval; input.prevInput = selected ? "" : "\u200b"; te.selectionStart = 1; te.selectionEnd = extval.length; // Re-set this, in case some other handler touched the // selection in the meantime. display.selForContextMenu = cm.doc.sel; } } function rehide() { if (input.contextMenuPending != rehide) { return } input.contextMenuPending = false; input.wrapper.style.cssText = oldWrapperCSS; te.style.cssText = oldCSS; if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } // Try to detect the user choosing select-all if (te.selectionStart != null) { if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } var i = 0, poll = function () { if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && te.selectionEnd > 0 && input.prevInput == "\u200b") { operation(cm, selectAll)(cm); } else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500); } else { display.selForContextMenu = null; display.input.reset(); } }; display.detectingSelectAll = setTimeout(poll, 200); } } if (ie && ie_version >= 9) { prepareSelectAllHack(); } if (captureRightClick) { e_stop(e); var mouseup = function () { off(window, "mouseup", mouseup); setTimeout(rehide, 20); }; on(window, "mouseup", mouseup); } else { setTimeout(rehide, 50); } }; TextareaInput.prototype.readOnlyChanged = function (val) { if (!val) { this.reset(); } this.textarea.disabled = val == "nocursor"; }; TextareaInput.prototype.setUneditable = function () {}; TextareaInput.prototype.needsContentAttribute = false; function fromTextArea(textarea, options) { options = options ? copyObj(options) : {}; options.value = textarea.value; if (!options.tabindex && textarea.tabIndex) { options.tabindex = textarea.tabIndex; } if (!options.placeholder && textarea.placeholder) { options.placeholder = textarea.placeholder; } // Set autofocus to true if this textarea is focused, or if it has // autofocus and no other element is focused. if (options.autofocus == null) { var hasFocus = activeElt(); options.autofocus = hasFocus == textarea || textarea.getAttribute("autofocus") != null && hasFocus == document.body; } function save() {textarea.value = cm.getValue();} var realSubmit; if (textarea.form) { on(textarea.form, "submit", save); // Deplorable hack to make the submit method do the right thing. if (!options.leaveSubmitMethodAlone) { var form = textarea.form; realSubmit = form.submit; try { var wrappedSubmit = form.submit = function () { save(); form.submit = realSubmit; form.submit(); form.submit = wrappedSubmit; }; } catch(e) {} } } options.finishInit = function (cm) { cm.save = save; cm.getTextArea = function () { return textarea; }; cm.toTextArea = function () { cm.toTextArea = isNaN; // Prevent this from being ran twice save(); textarea.parentNode.removeChild(cm.getWrapperElement()); textarea.style.display = ""; if (textarea.form) { off(textarea.form, "submit", save); if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") { textarea.form.submit = realSubmit; } } }; }; textarea.style.display = "none"; var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, options); return cm } function addLegacyProps(CodeMirror) { CodeMirror.off = off; CodeMirror.on = on; CodeMirror.wheelEventPixels = wheelEventPixels; CodeMirror.Doc = Doc; CodeMirror.splitLines = splitLinesAuto; CodeMirror.countColumn = countColumn; CodeMirror.findColumn = findColumn; CodeMirror.isWordChar = isWordCharBasic; CodeMirror.Pass = Pass; CodeMirror.signal = signal; CodeMirror.Line = Line; CodeMirror.changeEnd = changeEnd; CodeMirror.scrollbarModel = scrollbarModel; CodeMirror.Pos = Pos; CodeMirror.cmpPos = cmp; CodeMirror.modes = modes; CodeMirror.mimeModes = mimeModes; CodeMirror.resolveMode = resolveMode; CodeMirror.getMode = getMode; CodeMirror.modeExtensions = modeExtensions; CodeMirror.extendMode = extendMode; CodeMirror.copyState = copyState; CodeMirror.startState = startState; CodeMirror.innerMode = innerMode; CodeMirror.commands = commands; CodeMirror.keyMap = keyMap; CodeMirror.keyName = keyName; CodeMirror.isModifierKey = isModifierKey; CodeMirror.lookupKey = lookupKey; CodeMirror.normalizeKeyMap = normalizeKeyMap; CodeMirror.StringStream = StringStream; CodeMirror.SharedTextMarker = SharedTextMarker; CodeMirror.TextMarker = TextMarker; CodeMirror.LineWidget = LineWidget; CodeMirror.e_preventDefault = e_preventDefault; CodeMirror.e_stopPropagation = e_stopPropagation; CodeMirror.e_stop = e_stop; CodeMirror.addClass = addClass; CodeMirror.contains = contains; CodeMirror.rmClass = rmClass; CodeMirror.keyNames = keyNames; } // EDITOR CONSTRUCTOR defineOptions(CodeMirror); addEditorMethods(CodeMirror); // Set up methods on CodeMirror's prototype to redirect to the editor's document. var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) { CodeMirror.prototype[prop] = (function(method) { return function() {return method.apply(this.doc, arguments)} })(Doc.prototype[prop]); } } eventMixin(Doc); CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) CodeMirror.defineMode = function(name/*, mode, …*/) { if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } defineMode.apply(this, arguments); }; CodeMirror.defineMIME = defineMIME; // Minimal default mode. CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); CodeMirror.defineMIME("text/plain", "null"); // EXTENSIONS CodeMirror.defineExtension = function (name, func) { CodeMirror.prototype[name] = func; }; CodeMirror.defineDocExtension = function (name, func) { Doc.prototype[name] = func; }; CodeMirror.fromTextArea = fromTextArea; addLegacyProps(CodeMirror); CodeMirror.version = "5.49.2"; return CodeMirror; }))); admin/vendors/codemirror.css000064400000021001147600046700012171 0ustar00/* BASICS */ .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; height: 300px; color: black; direction: ltr; } /* PADDING */ .CodeMirror-lines { padding: 4px 0; /* Vertical padding around content */ } .CodeMirror pre.CodeMirror-line, .CodeMirror pre.CodeMirror-line-like { padding: 0 4px; /* Horizontal padding of content */ } .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { background-color: white; /* The little square between H and V scrollbars */ } /* GUTTER */ .CodeMirror-gutters { border-right: 1px solid #ddd; background-color: #f7f7f7; white-space: nowrap; } .CodeMirror-linenumbers {} .CodeMirror-linenumber { padding: 0 3px 0 5px; min-width: 20px; text-align: right; color: #999; white-space: nowrap; } .CodeMirror-guttermarker { color: black; } .CodeMirror-guttermarker-subtle { color: #999; } /* CURSOR */ .CodeMirror-cursor { border-left: 1px solid black; border-right: none; width: 0; } /* Shown when moving in bi-directional text */ .CodeMirror div.CodeMirror-secondarycursor { border-left: 1px solid silver; } .cm-fat-cursor .CodeMirror-cursor { width: auto; border: 0 !important; background: #7e7; } .cm-fat-cursor div.CodeMirror-cursors { z-index: 1; } .cm-fat-cursor-mark { background-color: rgba(20, 255, 20, 0.5); -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; } .cm-animate-fat-cursor { width: auto; border: 0; -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; background-color: #7e7; } @-moz-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @-webkit-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } /* Can style cursor different in overwrite (non-insert) mode */ .CodeMirror-overwrite .CodeMirror-cursor {} .cm-tab { display: inline-block; text-decoration: inherit; } .CodeMirror-rulers { position: absolute; left: 0; right: 0; top: -50px; bottom: 0; overflow: hidden; } .CodeMirror-ruler { border-left: 1px solid #ccc; top: 0; bottom: 0; position: absolute; } /* DEFAULT THEME */ .cm-s-default .cm-header {color: blue;} .cm-s-default .cm-quote {color: #090;} .cm-negative {color: #d44;} .cm-positive {color: #292;} .cm-header, .cm-strong {font-weight: bold;} .cm-em {font-style: italic;} .cm-link {text-decoration: underline;} .cm-strikethrough {text-decoration: line-through;} .cm-s-default .cm-keyword {color: #708;} .cm-s-default .cm-atom {color: #219;} .cm-s-default .cm-number {color: #164;} .cm-s-default .cm-def {color: #00f;} .cm-s-default .cm-variable, .cm-s-default .cm-punctuation, .cm-s-default .cm-property, .cm-s-default .cm-operator {} .cm-s-default .cm-variable-2 {color: #05a;} .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} .cm-s-default .cm-comment {color: #a50;} .cm-s-default .cm-string {color: #a11;} .cm-s-default .cm-string-2 {color: #f50;} .cm-s-default .cm-meta {color: #555;} .cm-s-default .cm-qualifier {color: #555;} .cm-s-default .cm-builtin {color: #30a;} .cm-s-default .cm-bracket {color: #997;} .cm-s-default .cm-tag {color: #170;} .cm-s-default .cm-attribute {color: #00c;} .cm-s-default .cm-hr {color: #999;} .cm-s-default .cm-link {color: #00c;} .cm-s-default .cm-error {color: #f00;} .cm-invalidchar {color: #f00;} .CodeMirror-composing { border-bottom: 2px solid; } /* Default styles for common addons */ div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } .CodeMirror-activeline-background {background: #e8f2ff;} /* STOP */ /* The rest of this file contains styles related to the mechanics of the editor. You probably shouldn't touch them. */ .CodeMirror { position: relative; overflow: hidden; background: white; } .CodeMirror-scroll { overflow: scroll !important; /* Things will break if this is overridden */ /* 30px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ margin-bottom: -30px; margin-right: -30px; padding-bottom: 30px; height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; } .CodeMirror-sizer { position: relative; border-right: 30px solid transparent; } /* The fake, visible scrollbars. Used to force redraw during scrolling before actual scrolling happens, thus preventing shaking and flickering artifacts. */ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { position: absolute; z-index: 6; display: none; } .CodeMirror-vscrollbar { right: 0; top: 0; overflow-x: hidden; overflow-y: scroll; } .CodeMirror-hscrollbar { bottom: 0; left: 0; overflow-y: hidden; overflow-x: scroll; } .CodeMirror-scrollbar-filler { right: 0; bottom: 0; } .CodeMirror-gutter-filler { left: 0; bottom: 0; } .CodeMirror-gutters { position: absolute; left: 0; top: 0; min-height: 100%; z-index: 3; } .CodeMirror-gutter { white-space: normal; height: 100%; display: inline-block; vertical-align: top; margin-bottom: -30px; } .CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: none !important; border: none !important; } .CodeMirror-gutter-background { position: absolute; top: 0; bottom: 0; z-index: 4; } .CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; } .CodeMirror-gutter-wrapper ::selection { background-color: transparent } .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } .CodeMirror-lines { cursor: text; min-height: 1px; /* prevents collapsing before first draw */ } .CodeMirror pre.CodeMirror-line, .CodeMirror pre.CodeMirror-line-like { /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; background: transparent; font-family: inherit; font-size: inherit; margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; color: inherit; z-index: 2; position: relative; overflow: visible; -webkit-tap-highlight-color: transparent; -webkit-font-variant-ligatures: contextual; font-variant-ligatures: contextual; } .CodeMirror-wrap pre.CodeMirror-line, .CodeMirror-wrap pre.CodeMirror-line-like { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } .CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0; } .CodeMirror-linewidget { position: relative; z-index: 2; padding: 0.1px; /* Force widget margins to stay inside of the container */ } .CodeMirror-widget {} .CodeMirror-rtl pre { direction: rtl; } .CodeMirror-code { outline: none; } /* Force content-box sizing for the elements where we expect it */ .CodeMirror-scroll, .CodeMirror-sizer, .CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber { -moz-box-sizing: content-box; box-sizing: content-box; } .CodeMirror-measure { position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden; } .CodeMirror-cursor { position: absolute; pointer-events: none; } .CodeMirror-measure pre { position: static; } div.CodeMirror-cursors { visibility: hidden; position: relative; z-index: 3; } div.CodeMirror-dragcursors { visibility: visible; } .CodeMirror-focused div.CodeMirror-cursors { visibility: visible; } .CodeMirror-selected { background: #d9d9d9; } .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } .CodeMirror-crosshair { cursor: crosshair; } .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } .cm-searching { background-color: #ffa; background-color: rgba(255, 255, 0, .4); } /* Used to force a border model for a node */ .cm-force-border { padding-right: .1px; } @media print { /* Hide the cursor when printing */ .CodeMirror div.CodeMirror-cursors { visibility: hidden; } } /* See issue #2901 */ .cm-tab-wrap-hack:after { content: ''; } /* Help users use markselection to safely style text background */ span.CodeMirror-selectedtext { background: none; } admin/index.php000064400000000032147600046700007453 0ustar00 */ class Cf7_Customizer_Admin { /** * The ID of this plugin. * * @since 1.0.0 * @access private * @var string $plugin_name The ID of this plugin. */ private $plugin_name; /** * The version of this plugin. * * @since 1.0.0 * @access private * @var string $version The current version of this plugin. */ private $version; /** * Initialize the class and set its properties. * * @since 1.0.0 * @param string $plugin_name The name of this plugin. * @param string $version The version of this plugin. */ public function __construct( $plugin_name, $version ) { $this->plugin_name = $plugin_name; $this->version = $version; } /** * Register the stylesheets for the admin area. * * @since 1.0.0 */ public function enqueue_styles() { if(isset($_GET['page']) && filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) == 'cf7cstmzr_page') { wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_style( 'codemirror', plugin_dir_url( __FILE__ ) . 'vendors/codemirror.css', array(), $this->version . time(), 'all' ); wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/cf7-customizer-admin.css', array(), $this->version . time(), 'all' ); } wp_enqueue_style( $this->plugin_name . '-global', plugin_dir_url( __FILE__ ) . 'css/cf7-customizer-admin-global.css', array(), $this->version . time(), 'all' ); } /** * Register the JavaScript for the admin area. * * @since 1.0.0 */ public function enqueue_scripts() { if(isset($_GET['page']) && (filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) == 'cf7cstmzr_page' || filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) == 'cf7cstmzr_tutorial_page')) { wp_enqueue_code_editor(array('type' => 'text/css')); if ( ! did_action( 'wp_enqueue_media' ) ) { wp_enqueue_media(); } $enable_for_form_nonce = wp_create_nonce('cf7cstmzr_enable_for_form_nonce'); $disable_for_form_nonce = wp_create_nonce('cf7cstmzr_disable_for_form_nonce'); $save_form_customizer_settings_nonce = wp_create_nonce('cf7cstmzr_save_form_customizer_settings_nonce'); $disable_globally_nonce = wp_create_nonce('cf7cstmzr_disable_globally_nonce'); $enable_globally_nonce = wp_create_nonce('cf7cstmzr_enable_globally_nonce'); $new_form_customizer_settings_nonce = wp_create_nonce('cf7cstmzr_new_form_customizer_settings_nonce'); $delete_form_customizer_settings_nonce = wp_create_nonce('cf7cstmzr_delete_form_customizer_settings_nonce'); $load_body_tag_nonce_nonce = wp_create_nonce('cf7cstmzr_load_body_tag_nonce_nonce'); wp_enqueue_script( 'codemirror', plugin_dir_url( __FILE__ ) . 'vendors/codemirror.js', array( 'jquery' ), '5.49.2', true ); wp_enqueue_script( 'jRespond', plugin_dir_url( __FILE__ ) . 'js/jRespond.js', array( 'jquery' ), '0.10', true ); wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/cf7-customizer-admin.js', array( 'jquery', 'jquery-ui-core', 'wp-color-picker' ), $this->version . time(), true ); // Localize the script with your nonce and the AJAX URL wp_localize_script($this->plugin_name, 'cf7cstmzr_ajax_object', array( 'enable_for_form_nonce' => $enable_for_form_nonce, 'disable_for_form_nonce' => $disable_for_form_nonce, 'save_form_customizer_settings_nonce' => $save_form_customizer_settings_nonce, 'disable_globally_nonce' => $disable_globally_nonce, 'enable_globally_nonce' => $enable_globally_nonce, 'new_form_customizer_settings_nonce' => $new_form_customizer_settings_nonce, 'delete_form_customizer_settings_nonce' => $delete_form_customizer_settings_nonce, 'load_body_tag_nonce' => $load_body_tag_nonce_nonce, )); } } public function settings_page() { $is_welcome_done = get_option('cf7cstmzr_welcome_done'); if (!cf7cstmzr_is_plugin_activated( 'contact-form-7', 'wp-contact-form-7.php' )) { add_menu_page( __( 'CF7 Styler', 'cf7-styler' ), __( 'CF7 Styler', 'cf7-styler' ), 'administrator', 'cf7cstmzr_page', array($this, 'render_settings_page'), 'dashicons-email-alt', 6 ); if (!empty($is_welcome_done)) { add_submenu_page( 'cf7cstmzr_page', __( 'Support & KB', 'cf7-styler' ), __( 'Support & KB', 'cf7-styler' ), 'administrator', 'cf7cstmzrcf7cstmzr_tutorial_page', array($this, 'render_tutorials_page') ); } } else { add_submenu_page( 'wpcf7', __( 'CF7 Styler', 'cf7-styler' ), __( 'CF7 Styler', 'cf7-styler' ), 'administrator', 'cf7cstmzr_page', array($this, 'render_settings_page') ); if (!empty($is_welcome_done)) { add_submenu_page( 'wpcf7', __( 'Support & KB', 'cf7-styler' ), ''.__( 'Support & KB', 'cf7-styler' ).'', 'administrator', 'cf7cstmzr_tutorial_page', array($this, 'render_settings_page') ); } } } public function render_settings_page() { $is_welcome_done = get_option('cf7cstmzr_welcome_done'); if (!empty($_GET['page']) && ('cf7cstmzr_tutorial_page' === filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) || empty($is_welcome_done))) { include_once CF7CSTMZR_PLUGIN_PATH . 'admin/partials/cf7-customizer-admin-tutorial.php'; } else { include_once CF7CSTMZR_PLUGIN_PATH . 'admin/partials/cf7-customizer-admin-display.php'; } } public function check_installation() { $style_schemes = get_option('cf7cstmzr_style_schemes', array()); if (empty($style_schemes)) { $default_scheme = array( 'default' => Cf7_Style_Scheme::get_default_style_scheme() ); update_option('cf7cstmzr_style_schemes', $default_scheme); } } public function rewrite_rule() { flush_rewrite_rules(); add_rewrite_rule('^cf7cstmzr-form-customizer/(.*)/?', 'index.php?cf7cstmzr-form=$matches[1]', 'top'); add_rewrite_tag( '%cf7cstmzr-form%', '([^&]+)' ); } public function check_version() { $plugin_version = Cf7_License::get_license_version(); if ('free' === $plugin_version) { delete_option('cf7cstmzr_enabled_globally'); $individually_styled_forms = Cf7_Style_Scheme::get_individually_styled_forms(); } } public function template_redirect() { $permalink_structure = get_option('permalink_structure'); if (!empty($permalink_structure)) { $form_id =sanitize_text_field( get_query_var( 'cf7cstmzr-form' )); } else { if (!empty(sanitize_text_field($_GET['cf7cstmzr_page'])) && !empty(sanitize_text_field($_GET['form_id']))) { $form_id = filter_input( INPUT_GET, 'form_id', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); } } if ($form_id) { $form_post = get_post($form_id); if (!$form_post || 'publish' !== $form_post->post_status) { die; } global $content_width; if (empty($content_width)) { $content_width = 640; } $content_width = $content_width . 'px'; $content_width = '90%'; $form = get_post($form_id); get_header(); if (!empty($form)) { $preview_mode = get_option('cf7cstmzr-preview-mode', false); $is_split_mode = $preview_mode === 'split-mode'; ?>
post_title . '"]'); ?>
post_title . '"]'); ?>
post_title . '"]'); } ?>
__('CF7 Styler', 'cf7-styler'), 'callback' => array($this, 'show_metabox') ); return $panels; } public function show_metabox($contact_form) { $plugin_version = Cf7_License::get_license_version(); $style_schemes = get_option('cf7cstmzr_style_schemes', array()); $form_id = $contact_form->id(); $selected_style = get_post_meta($form_id, 'cf7cstmzr_style_scheme', true); $individually_styled_forms = Cf7_Style_Scheme::get_individually_styled_forms(); if ('free' === $plugin_version && !empty($individually_styled_forms)) { foreach ($individually_styled_forms as $styled_form_id => $styled_form_style) { $styled_form = get_post($styled_form_id); if (!empty($styled_form)) { $styled_form_title = $styled_form->post_title; $styled_form_style_title = $style_schemes[$styled_form_style]['title']; } } } if (!empty($style_schemes)) { ?>

%s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.', 'cf7-styler' ), $styled_form_title, $styled_form_style_title ); ?>

postmeta} WHERE meta_key='cf7cstmzr_style_scheme';"; $wpdb->query($sql); } update_post_meta( $post_ID, 'cf7cstmzr_style_scheme', $cf7cstmzr_style_scheme ); } else { delete_post_meta($post_ID, 'cf7cstmzr_style_scheme'); } } public function plugin_menu_optin() { global $submenu; if (function_exists('cf7_styler')) { $reconnect_url = cf7_styler()->get_activation_url( array( 'nonce' => wp_create_nonce( cf7_styler()->get_unique_affix() . '_reconnect' ), 'fs_action' => ( cf7_styler()->get_unique_affix() . '_reconnect' ), ) ); $is_registered = cf7_styler()->is_registered(); if (!$is_registered && isset($submenu["wpcf7"])) { $submenu["wpcf7"][] = array( '' . __('Opt-in to see account', 'cf7-styler') . '', 'manage_options', $reconnect_url ); } } } } admin/class-cf7-customizer-admin-ajax.php000064400000051040147600046700014344 0ustar00 */ class Cf7_Customizer_Admin_Ajax { /** * The ID of this plugin. * * @since 1.0.0 * @access private * @var string $plugin_name The ID of this plugin. */ private $plugin_name; /** * The version of this plugin. * * @since 1.0.0 * @access private * @var string $version The current version of this plugin. */ private $version; /** * Initialize the class and set its properties. * * @since 1.0.0 * @param string $plugin_name The name of this plugin. * @param string $version The version of this plugin. */ public function __construct( $plugin_name, $version ) { $this->plugin_name = $plugin_name; $this->version = $version; } public function save_form_customizer_settings() { check_ajax_referer( 'cf7cstmzr_save_form_customizer_settings_nonce', 'nonce' ); $plugin_version = Cf7_License::get_license_version(); $style_schemes = get_option('cf7cstmzr_style_schemes', array()); if (empty($style_schemes)) { $create_default = true; } $style_scheme_slug = !empty($_POST['styleSchemeSlug']) ? sanitize_text_field($_POST['styleSchemeSlug']) : 'default'; $style_scheme_title = !empty($_POST['styleSchemeTitle']) ? sanitize_text_field($_POST['styleSchemeTitle']) : __('Default Scheme', 'cf7-styler'); $form_data_array = array(); if (!empty($_POST['formData'])) { foreach ($_POST['formData'] as $form_data) { $name = str_replace('cf7cstmzr_', '', sanitize_text_field($form_data['name'])); $forbidden_names = array(); if ('free' === $plugin_version) { $forbidden_names = array( 'form_bg_img', 'form_bg_img-position', 'form_bg_img-opacity', 'form_bg_img-size', 'checkbox_full-width', 'radiobutton_full-width', ); } if (in_array($name, $forbidden_names)) { $value = ''; } elseif ('custom_css' === $name) { $value = sanitize_textarea_field($form_data['value']); } else { $value = sanitize_text_field($form_data['value']); } if (!empty($value)) { $name_array = explode('_', $name); if (!empty($name_array[4])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]][$name_array[3]][$name_array[4]] = $value; } elseif (!empty($name_array[3])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]][$name_array[3]] = $value; } elseif (!empty($name_array[2])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]] = $value; } elseif (!empty($name_array[1])) { $form_data_array[$name_array[0]][$name_array[1]] = $value; } } } } $style_scheme = array( 'title' => $style_scheme_title, 'scheme' => $form_data_array, ); $style_schemes[$style_scheme_slug] = $style_scheme; update_option('cf7cstmzr_style_schemes', $style_schemes); $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . __('Saved', 'cf7-styler')); if (!empty($create_default)) { $response['url'] = get_site_url() . '/wp-admin/admin.php?page=cf7cstmzr_page&tab=form-customize'; } echo json_encode($response); wp_die(); } public function new_form_customizer_settings() { check_ajax_referer( 'cf7cstmzr_new_form_customizer_settings_nonce', 'nonce' ); $title = !empty(sanitize_text_field($_POST['title'])) ? sanitize_text_field($_POST['title']) : false; if (!$title) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('Please input style scheme title', 'cf7-styler')); echo json_encode($response); wp_die(); } if (!empty($_POST['copySettings']) && 'true' === $_POST['copySettings']) { $copySettings = true; } else { $copySettings = false; } $plugin_version = Cf7_License::get_license_version(); $style_schemes = get_option('cf7cstmzr_style_schemes', array()); $style_scheme_slug = 'cf7cstmzr_' . time(); $style_scheme_title = $title; $form_data_array = array(); if (!empty($_POST['formData'])) { foreach ($_POST['formData'] as $form_data) { $name = str_replace('cf7cstmzr_', '', sanitize_text_field($form_data['name'])); if ($copySettings) { $forbidden_names = array(); if ('free' === $plugin_version) { $forbidden_names = array( 'form_bg_img', 'form_bg_img-position', 'form_bg_img-opacity', 'form_bg_img-size', 'checkbox_full-width', 'radiobutton_full-width', ); } if (in_array($name, $forbidden_names)) { $value = ''; } elseif ('custom_css' === $name) { $value = sanitize_textarea_field($form_data['value']); } else { $value = sanitize_text_field($form_data['value']); } } else { $value = ''; } if (!empty($value)) { $name_array = explode('_', $name); if (!empty($name_array[4])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]][$name_array[3]][$name_array[4]] = $value; } elseif (!empty($name_array[3])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]][$name_array[3]] = $value; } elseif (!empty($name_array[2])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]] = $value; } elseif (!empty($name_array[1])) { $form_data_array[$name_array[0]][$name_array[1]] = $value; } } } } $style_scheme = array( 'title' => $style_scheme_title, 'scheme' => $form_data_array, ); $isFw = !empty($_POST['isFw']) && 'true' === $_POST['isFw'] ? '&fw=1' : ''; $style_schemes[$style_scheme_slug] = $style_scheme; update_option('cf7cstmzr_style_schemes', $style_schemes); $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . __('Settings saved as ', 'cf7-styler') . $title, 'url' => get_site_url() . '/wp-admin/admin.php?page=cf7cstmzr_page&tab=form-customize&style_scheme=' . $style_scheme_slug . $isFw); echo json_encode($response); wp_die(); } public function close_welcome() { update_option('cf7cstmzr_welcome_done', '1'); $response = array('success' => 1, 'error' => 0, 'url' => get_site_url() . '/wp-admin/admin.php?page=cf7cstmzr_page'); echo json_encode($response); wp_die(); } public function delete_form_customizer_settings() { check_ajax_referer( 'cf7cstmzr_delete_form_customizer_settings_nonce', 'nonce' ); $scheme = !empty(sanitize_text_field($_POST['scheme'])) ? sanitize_text_field($_POST['scheme']) : false; if (!$scheme) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('No style scheme selected', 'cf7-styler')); echo json_encode($response); wp_die(); } if ('default' === $scheme) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('You can not delete', 'cf7-styler') . ' ' . __('Default Scheme', 'cf7-styler')); echo json_encode($response); wp_die(); } $style_schemes = get_option('cf7cstmzr_style_schemes', array()); if (!empty($style_schemes[$scheme])) { $title = $style_schemes[$scheme]['title']; unset ($style_schemes[$scheme]); update_option('cf7cstmzr_style_schemes', $style_schemes); $enabled_globally = get_option('cf7cstmzr_enabled_globally', ''); if ($enabled_globally === $scheme) { delete_option('cf7cstmzr_enabled_globally'); } $cf7_scheme_args = array ( 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC', 'post_type' => 'wpcf7_contact_form', 'post_status' => 'publish', 'suppress_filters' => false, // подавление работы фильтров изменения SQL запроса 'meta_query' => array( 'relation' => 'LIKE', array( 'key' => 'cf7cstmzr_style_scheme', 'value' => $scheme, ) ) ); $cf7_scheme_forms = get_posts($cf7_scheme_args); if (!empty($cf7_scheme_forms)) { foreach ($cf7_scheme_forms as $form) { delete_post_meta( $form->ID, 'cf7cstmzr_style_scheme' ); } } $isFw = !empty($_POST['isFw']) && 'true' === $_POST['isFw'] ? '&fw=1' : ''; $response = array( 'success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . $title . ' ' . __('deleted', 'cf7-styler'), 'url' => get_site_url() . '/wp-admin/admin.php?page=cf7cstmzr_page&tab=form-customize' . $isFw ); echo json_encode($response); wp_die(); } } public function load_body_tag() { check_ajax_referer( 'cf7cstmzr_load_body_tag_nonce_nonce', 'nonce' ); if (empty($_POST['loadBody']) || 'true' === sanitize_text_field($_POST['loadBody']) ) { update_option('cf7cstmzr-load-body-tag', 1); $confirm = __('Style will be loaded in tag', 'cf7-styler'); } else { update_option('cf7cstmzr-load-body-tag', 0); $confirm = __('Style will be loaded in tag', 'cf7-styler'); } $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . $confirm ); echo json_encode($response); wp_die(); } public function preview_form_customizer_settings() { delete_option('cf7cstmzr_style_schemes_preview'); $style_schemes = array(); $style_scheme_slug = 'preview'; $style_scheme_title = 'preview'; $form_data_array = array(); if (empty($_POST['unstyle']) || 'true' === sanitize_text_field($_POST['unstyle']) ) { update_option('cf7cstmzr-preview-styled', 0); } else { update_option('cf7cstmzr-preview-styled', 1); } if (!empty($_POST['previewMode']) ) { update_option('cf7cstmzr-preview-mode', sanitize_text_field($_POST['previewMode'])); } if (!empty($_POST['splitModeValue']) ) { update_option('cf7cstmzr-split-mode', sanitize_text_field($_POST['splitModeValue'])); } if (!empty($_POST['formData'])) { foreach ($_POST['formData'] as $form_data) { $name = str_replace('cf7cstmzr_', '', sanitize_text_field($form_data['name'])); if ('custom_css' === $name) { $value = sanitize_textarea_field($form_data['value']); } else { $value = sanitize_text_field($form_data['value']); } if (!empty($value)) { $name_array = explode('_', $name); if (!empty($name_array[4])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]][$name_array[3]][$name_array[4]] = $value; } elseif (!empty($name_array[3])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]][$name_array[3]] = $value; } elseif (!empty($name_array[2])) { $form_data_array[$name_array[0]][$name_array[1]][$name_array[2]] = $value; } elseif (!empty($name_array[1])) { $form_data_array[$name_array[0]][$name_array[1]] = $value; } } } } $style_scheme = array( 'title' => $style_scheme_title, 'scheme' => $form_data_array, ); $style_schemes[$style_scheme_slug] = $style_scheme; update_option('cf7cstmzr_style_schemes_preview', $style_schemes); $response = array('success' => 1, 'error' => 0); echo json_encode($response); wp_die(); } public function enable_globally() { check_ajax_referer( 'cf7cstmzr_enable_globally_nonce', 'nonce' ); $scheme = !empty($_POST['scheme']) ? sanitize_text_field($_POST['scheme']) : false; if (!$scheme) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('Style scheme is not selected', 'cf7-styler')); echo json_encode($response); wp_die(); } $style_schemes = get_option('cf7cstmzr_style_schemes', array()); if (empty($style_schemes[$scheme])) { $response = array('success' => 0, 'error' => 1, 'message' => __('Success', 'cf7-styler') . ': ' . __('This scheme is not existed', 'cf7-styler')); echo json_encode($response); wp_die(); } update_option('cf7cstmzr_enabled_globally', $scheme); $title = $style_schemes[$scheme]['title']; $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . $title . ' ' . __('enabled for all forms', 'cf7-styler')); echo json_encode($response); wp_die(); } public function disable_globally() { check_ajax_referer( 'cf7cstmzr_disable_globally_nonce', 'nonce' ); delete_option('cf7cstmzr_enabled_globally'); $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . __('Style scheme disabled for all forms', 'cf7-styler')); echo json_encode($response); wp_die(); } public function cache_form() { $form = !empty($_POST['form']) ? sanitize_text_field($_POST['form']) : false; if (!$form) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('Form is not selected', 'cf7-styler')); echo json_encode($response); wp_die(); } update_option( 'cf7cstmzr_cache_form', $form); $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . __('Style scheme enabled for this form', 'cf7-styler')); echo json_encode($response); wp_die(); } public function enable_for_form() { check_ajax_referer( 'cf7cstmzr_enable_for_form_nonce', 'nonce' ); $plugin_version = Cf7_License::get_license_version(); $scheme = !empty($_POST['scheme']) ? sanitize_text_field($_POST['scheme']) : false; if (!$scheme) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('Style scheme is not selected', 'cf7-styler')); echo json_encode($response); wp_die(); } $form = !empty($_POST['form']) ? sanitize_text_field($_POST['form']) : false; if (!$form) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('Form is not selected', 'cf7-styler')); echo json_encode($response); wp_die(); } if ('free' === $plugin_version) { global $wpdb; $sql = "DELETE FROM {$wpdb->postmeta} WHERE meta_key='cf7cstmzr_style_scheme';"; $wpdb->query($sql); } update_post_meta( $form, 'cf7cstmzr_style_scheme', $scheme ); $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . __('Style scheme enabled for this form', 'cf7-styler')); echo json_encode($response); wp_die(); } public function disable_for_form() { check_ajax_referer( 'cf7cstmzr_disable_for_form_nonce', 'nonce' ); $form = !empty($_POST['form']) ? sanitize_text_field($_POST['form']) : false; if (!$form) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('Form is not selected', 'cf7-styler')); echo json_encode($response); wp_die(); } delete_post_meta( $form, 'cf7cstmzr_style_scheme' ); $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . __('Style scheme disabled for this form', 'cf7-styler')); echo json_encode($response); wp_die(); } public function change_form_preview() { $form_id = !empty($_POST['formId']) ? sanitize_text_field($_POST['formId']) : false; if (!$form_id) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('CF7 Form is not selected', 'cf7-styler')); echo json_encode($response); wp_die(); } $form_preview = Cf7_Style_Scheme::get_form_preview($form_id); $content = wp_remote_get('http://cf7-customizer.loc/cf7cstmzr-form-customizer/' . $form_id); $fragments = array ( '#form-preview-container' => $form_preview, ); // $response = array('success' => 1, 'error' => 0, 'fragments' => $fragments); $response = array( 'success' => 1, 'error' => 0, 'src' => 'http://cf7-customizer.loc/cf7cstmzr-form-customizer/' . $form_id, 'content' => $content['body'], ); echo json_encode($response); wp_die(); } public function install_plugin() { $plugin = isset($_POST['plugin']) ? sanitize_text_field($_POST['plugin']) : ''; if (empty($plugin)) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('No plugin selected to install', 'cf7-styler')); echo json_encode($response); wp_die(); } $required_plugins = Cf7_Required_Plugin::get_required_plugins(); if (empty($required_plugins[$plugin])) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'wp2leads-wtsr') . ': ' . __('Plugin you are trying to install is not in the required list', 'wp2leads-wtsr')); echo json_encode($response); wp_die(); } $result = Cf7_Required_Plugin::install_and_activate_plugin($plugin); if (!$result) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'wp2leads-wtsr') . ': ' . __('Plugin could not be installed manually', 'wp2leads-wtsr')); echo 'error'; echo '&&&&&'; echo json_encode($response); wp_die(); } $plugin_name = $required_plugins[$plugin]['label']; $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'wp2leads-wtsr') . ': ' . $plugin_name . __(' installed and activated', 'wp2leads-wtsr')); echo 'success'; echo '&&&&&'; echo json_encode($response); wp_die(); } } freemius/assets/css/admin/plugins.css000064400000001001147600046700013734 0ustar00label.fs-tag,span.fs-tag{background:#ffba00;border-radius:3px;color:#fff;display:inline-block;font-size:11px;line-height:11px;padding:5px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.wp-list-table.plugins .plugin-title span.fs-tag{display:inline-block;line-height:10px;margin-left:5px}freemius/assets/css/admin/optout.css000064400000007320147600046700013617 0ustar00.fs-tooltip-trigger{position:relative}.fs-tooltip-trigger:not(a){cursor:help}.fs-tooltip-trigger .dashicons{float:none!important}.fs-tooltip-trigger .fs-tooltip{background:rgba(0,0,0,.8);border-radius:5px;bottom:100%;box-shadow:1px 1px 1px rgba(0,0,0,.2);color:#fff!important;font-family:arial,serif;font-size:12px;font-weight:700;left:-17px;line-height:1.3em;margin-bottom:5px;opacity:0;padding:10px;position:absolute;right:0;text-align:left;text-transform:none!important;transition:opacity .3s ease-in-out;visibility:hidden;z-index:999999}.rtl .fs-tooltip-trigger .fs-tooltip{left:auto;right:-17px;text-align:right}.fs-tooltip-trigger .fs-tooltip:after{border-color:rgba(0,0,0,.8) transparent transparent;border-style:solid;border-width:5px 5px 0;content:" ";display:block;height:0;left:21px;position:absolute;top:100%;width:0}.rtl .fs-tooltip-trigger .fs-tooltip:after{left:auto;right:21px}.fs-tooltip-trigger:hover .fs-tooltip{opacity:1;visibility:visible}.fs-permissions .fs-permission.fs-disabled,.fs-permissions .fs-permission.fs-disabled .fs-permission-description span{color:#aaa}.fs-permissions .fs-permission .fs-switch-feedback{position:absolute;right:15px;top:52px}.fs-permissions ul{height:0;margin:0;overflow:hidden}.fs-permissions ul li{margin:0;padding:17px 15px;position:relative}.fs-permissions ul li>i.dashicons{float:left;font-size:30px;height:30px;padding:5px;width:30px}.fs-permissions ul li .fs-switch{float:right}.fs-permissions ul li .fs-permission-description{margin-left:55px}.fs-permissions ul li .fs-permission-description span{color:#23282d;font-size:14px;font-weight:500}.fs-permissions ul li .fs-permission-description .fs-tooltip{font-size:13px;font-weight:700}.fs-permissions ul li .fs-permission-description .fs-tooltip-trigger .dashicons{margin:-1px 2px 0}.fs-permissions ul li .fs-permission-description p{margin:2px 0 0}.fs-permissions.fs-open{background:#fff}.fs-permissions.fs-open ul{height:auto;margin:20px 0 10px;overflow:initial}.fs-permissions .fs-switch-feedback .fs-ajax-spinner{margin-right:10px}.fs-permissions .fs-switch-feedback.success{color:#71ae00}.rtl .fs-permissions .fs-switch-feedback{left:15px;right:auto}.rtl .fs-permissions .fs-switch-feedback .fs-ajax-spinner{margin-left:10px;margin-right:0}.rtl .fs-permissions ul li .fs-permission-description{margin-left:0;margin-right:55px}.rtl .fs-permissions ul li .fs-switch{float:left}.rtl .fs-permissions ul li i.dashicons{float:right}.fs-modal-opt-out .fs-modal-footer .fs-opt-out-button{line-height:30px;margin-right:10px}.fs-modal-opt-out .fs-permissions{margin-top:0!important}.fs-modal-opt-out .fs-permissions .fs-permissions-section--header .fs-group-opt-out-button{float:right;line-height:1.1em}.fs-modal-opt-out .fs-permissions .fs-permissions-section--header .fs-switch-feedback{float:right;line-height:1.1em;margin-right:10px}.fs-modal-opt-out .fs-permissions .fs-permissions-section--header .fs-switch-feedback .fs-ajax-spinner{margin:-2px 0 0}.fs-modal-opt-out .fs-permissions .fs-permissions-section--header-title{display:block;font-size:1.1em;font-weight:600;line-height:1.1em;margin:.5em 0;text-transform:uppercase}.fs-modal-opt-out .fs-permissions .fs-permissions-section--desc{margin-top:0}.fs-modal-opt-out .fs-permissions hr{border:0;border-top:1px solid #eee;margin:25px 0 20px}.fs-modal-opt-out .fs-permissions ul{border:1px solid #c3c4c7;border-radius:3px;box-shadow:0 1px 1px rgba(0,0,0,.04);margin:10px 0 0}.fs-modal-opt-out .fs-permissions ul li{border-bottom:1px solid #d7dde1;border-left:4px solid #72aee6}.rtl .fs-modal-opt-out .fs-permissions ul li{border-left:none;border-right:4px solid #72aee6}.fs-modal-opt-out .fs-permissions ul li.fs-disabled{border-left-color:rgba(114,174,230,0)}.fs-modal-opt-out .fs-permissions ul li:last-child{border-bottom:none}freemius/assets/css/admin/index.php000064400000000127147600046700013371 0ustar00h3>strong{font-size:1.3em}}.fs-modal.active,.fs-modal.active:before{display:block}.fs-modal.active .fs-modal-dialog{top:10%}.fs-modal.fs-success .fs-modal-header{border-bottom-color:#46b450}.fs-modal.fs-success .fs-modal-body{background-color:#f7fff7}.fs-modal.fs-warn .fs-modal-header{border-bottom-color:#ffb900}.fs-modal.fs-warn .fs-modal-body{background-color:#fff8e5}.fs-modal.fs-error .fs-modal-header{border-bottom-color:#dc3232}.fs-modal.fs-error .fs-modal-body{background-color:#ffeaea}.fs-modal .fs-modal-body,.fs-modal .fs-modal-footer{background:#fefefe;border:0;padding:20px}.fs-modal .fs-modal-header{background:#fbfbfb;border-bottom:1px solid #eee;margin-bottom:-10px;padding:15px 20px;position:relative}.fs-modal .fs-modal-header h4{color:#cacaca;font-size:1.2em;font-weight:700;letter-spacing:.6px;margin:0;padding:0;text-shadow:1px 1px 1px #fff;text-transform:uppercase;-webkit-font-smoothing:antialiased}.fs-modal .fs-modal-header .fs-close{border-radius:20px;color:#bbb;cursor:pointer;padding:3px;position:absolute;right:10px;top:12px;transition:all .2s ease-in-out}.fs-modal .fs-modal-header .fs-close:hover{background:#aaa;color:#fff}.fs-modal .fs-modal-header .fs-close .dashicons,.fs-modal .fs-modal-header .fs-close:hover .dashicons{text-decoration:none}.fs-modal .fs-modal-body{border-bottom:0}.fs-modal .fs-modal-body p{font-size:14px}.fs-modal .fs-modal-body h2{font-size:20px;line-height:1.5em}.fs-modal .fs-modal-body>div{margin-top:10px}.fs-modal .fs-modal-body>div h2{font-size:20px;font-weight:700;margin-top:0}.fs-modal .fs-modal-footer{border-top:1px solid #eee;text-align:right}.fs-modal .fs-modal-footer>.button{margin:0 7px}.fs-modal .fs-modal-footer>.button:last-of-type{margin:0}.fs-modal .fs-modal-panel>.notice.inline{display:none;margin:0}.fs-modal .fs-modal-panel:not(.active){display:none}.rtl .fs-modal .fs-modal-header .fs-close{left:20px;right:auto}.rtl .fs-modal .fs-modal-footer{text-align:left}body.has-fs-modal{overflow:hidden}.fs-modal.fs-modal-deactivation-feedback .internal-message,.fs-modal.fs-modal-deactivation-feedback .reason-input{margin:3px 0 3px 22px}.fs-modal.fs-modal-deactivation-feedback .internal-message input,.fs-modal.fs-modal-deactivation-feedback .internal-message textarea,.fs-modal.fs-modal-deactivation-feedback .reason-input input,.fs-modal.fs-modal-deactivation-feedback .reason-input textarea{width:100%}.fs-modal.fs-modal-deactivation-feedback li.reason.has-internal-message .internal-message{border:1px solid #ccc;display:none;padding:7px}@media(max-width:650px){.fs-modal.fs-modal-deactivation-feedback li.reason li.reason{margin-bottom:10px}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason .internal-message,.fs-modal.fs-modal-deactivation-feedback li.reason li.reason .reason-input{margin-left:29px}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason label{display:table}.fs-modal.fs-modal-deactivation-feedback li.reason li.reason label>span{display:table-cell;font-size:1.3em}}.fs-modal.fs-modal-deactivation-feedback .anonymous-feedback-label,.fs-modal.fs-modal-deactivation-feedback .feedback-from-snooze-label{float:left;line-height:30px}.rtl .fs-modal.fs-modal-deactivation-feedback .anonymous-feedback-label,.rtl .fs-modal.fs-modal-deactivation-feedback .feedback-from-snooze-label{float:right}.fs-modal.fs-modal-deactivation-feedback .fs-modal-panel{margin-top:0!important}.fs-modal.fs-modal-deactivation-feedback .fs-modal-panel h3{font-size:16px;line-height:1.5em;margin-bottom:0;margin-top:10px}#the-list .deactivate>.fs-slug{display:none}.fs-modal.fs-modal-subscription-cancellation .fs-price-increase-warning{color:red;font-weight:700;margin-bottom:0;padding:0 25px}.fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label input{float:left;position:relative;top:5px}.rtl .fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label input{float:right}.fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label span{display:block;margin-left:24px}.rtl .fs-modal.fs-modal-subscription-cancellation ul.subscription-actions label span{margin-left:0;margin-right:24px}.fs-license-options-container table,.fs-license-options-container table .fs-available-license-key,.fs-license-options-container table select,.fs-modal.fs-modal-license-activation .fs-modal-body input.fs-license-key{width:100%}.fs-license-options-container table td:first-child{width:1%}.fs-license-options-container table .fs-other-license-key-container label{float:left;margin-right:5px;position:relative;top:6px}.fs-license-options-container table .fs-other-license-key-container div{display:block;height:30px;overflow:hidden;position:relative;top:2px;width:auto}.fs-license-options-container table .fs-other-license-key-container div input{margin:0}.fs-sites-list-container td{cursor:pointer}.fs-modal.fs-modal-user-change .fs-modal-body input#fs_other_email_address{width:100%}.fs-user-change-options-container table{border-collapse:collapse;width:100%}.fs-user-change-options-container table tr{display:block;margin-bottom:2px}.fs-user-change-options-container table .fs-email-address-container td{display:inline-block}.fs-user-change-options-container table .fs-email-address-container input[type=radio]{margin-bottom:0;margin-top:0}.fs-user-change-options-container table .fs-other-email-address-container{width:100%}.fs-user-change-options-container table .fs-other-email-address-container>div{display:table;width:100%}.fs-user-change-options-container table .fs-other-email-address-container>div label,.fs-user-change-options-container table .fs-other-email-address-container>div>div{display:table-cell}.fs-user-change-options-container table .fs-other-email-address-container>div label{padding-left:3px;padding-right:3px;width:1%}.fs-user-change-options-container table .fs-other-email-address-container>div>div{width:auto}.fs-modal.fs-modal-developer-license-debug-mode .fs-modal-body input.fs-license-or-user-key,.fs-user-change-options-container table .fs-other-email-address-container>div>div input{width:100%}.fs-multisite-options-container{border:1px solid #ccc;margin-top:20px;padding:5px}.fs-multisite-options-container a{text-decoration:none}.fs-multisite-options-container a:focus{box-shadow:none}.fs-multisite-options-container a.selected{font-weight:700}.fs-multisite-options-container.fs-apply-on-all-sites{border:0;padding:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options{border-spacing:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options td:not(:first-child){display:none}.fs-multisite-options-container .fs-sites-list-container{display:none;overflow:auto}.fs-multisite-options-container .fs-sites-list-container table td{border-top:1px solid #ccc;padding:4px 2px}.fs-modal.fs-modal-license-key-resend .email-address-container{overflow:hidden;padding-right:2px}.fs-modal.fs-modal-license-key-resend.fs-freemium input.email-address{width:300px}.fs-modal.fs-modal-license-key-resend.fs-freemium label{display:block;margin-bottom:10px}.fs-modal.fs-modal-license-key-resend.fs-premium input.email-address{width:100%}.fs-modal.fs-modal-license-key-resend.fs-premium .button-container{float:right;margin-left:7px}@media(max-width:650px){.fs-modal.fs-modal-license-key-resend.fs-premium .button-container{margin-top:2px}}.rtl .fs-modal.fs-modal-license-key-resend .fs-modal-body .input-container>.email-address-container{padding-left:2px;padding-right:0}.rtl .fs-modal.fs-modal-license-key-resend .fs-modal-body .button-container{float:left;margin-left:0;margin-right:7px}a.show-license-resend-modal{display:inline-block;margin-top:4px}.fs-modal.fs-modal-email-address-update .fs-modal-body input[type=text]{width:100%}.fs-modal.fs-modal-email-address-update p{margin-bottom:0}.fs-modal.fs-modal-email-address-update ul{margin:1em .5em}.fs-modal.fs-modal-email-address-update ul li label span{float:left;margin-top:0}.fs-modal.fs-modal-email-address-update ul li label span:last-child{display:block;float:none;margin-left:20px}.fs-ajax-loader{height:20px;margin:auto;position:relative;width:170px}.fs-ajax-loader .fs-ajax-loader-bar{animation-direction:normal;animation-duration:1.5s;animation-iteration-count:infinite;animation-name:bounce_ajaxLoader;background-color:#fff;height:20px;position:absolute;top:0;transform:scale(.3);width:20px}.fs-ajax-loader .fs-ajax-loader-bar-1{animation-delay:.6s;left:0}.fs-ajax-loader .fs-ajax-loader-bar-2{animation-delay:.75s;left:19px}.fs-ajax-loader .fs-ajax-loader-bar-3{animation-delay:.9s;left:38px}.fs-ajax-loader .fs-ajax-loader-bar-4{animation-delay:1.05s;left:57px}.fs-ajax-loader .fs-ajax-loader-bar-5{animation-delay:1.2s;left:76px}.fs-ajax-loader .fs-ajax-loader-bar-6{animation-delay:1.35s;left:95px}.fs-ajax-loader .fs-ajax-loader-bar-7{animation-delay:1.5s;left:114px}.fs-ajax-loader .fs-ajax-loader-bar-8{animation-delay:1.65s;left:133px}@keyframes bounce_ajaxLoader{0%{background-color:#0074a3;transform:scale(1)}to{background-color:#fff;transform:scale(.3)}}.fs-modal-auto-install #request-filesystem-credentials-form .request-filesystem-credentials-action-buttons,.fs-modal-auto-install #request-filesystem-credentials-form h2{display:none}.fs-modal-auto-install #request-filesystem-credentials-form input[type=email],.fs-modal-auto-install #request-filesystem-credentials-form input[type=password],.fs-modal-auto-install #request-filesystem-credentials-form input[type=text]{-webkit-appearance:none;max-width:100%;padding:10px 10px 5px;width:300px}.fs-modal-auto-install #request-filesystem-credentials-form fieldset,.fs-modal-auto-install #request-filesystem-credentials-form label,.fs-modal-auto-install #request-filesystem-credentials-form>div{display:block;margin:0 auto;max-width:100%;width:300px}.button-primary.warn{background:#f56a48;border-color:#ec6544 #d2593c #d2593c;box-shadow:0 1px 0 #d2593c;text-shadow:0 -1px 1px #d2593c,1px 0 1px #d2593c,0 1px 1px #d2593c,-1px 0 1px #d2593c}.button-primary.warn:hover{background:#fd6d4a;border-color:#d2593c}.button-primary.warn:focus{box-shadow:0 1px 0 #dd6041,0 0 2px 1px #e4a796}.button-primary.warn:active{background:#dd6041;border-color:#d2593c;box-shadow:inset 0 2px 0 #d2593c}.button-primary.warn.disabled{background:#e76444!important;border-color:#d85e40!important;color:#f5b3a1!important;text-shadow:0 -1px 0 rgba(0,0,0,.1)!important}freemius/assets/css/admin/debug.css000064400000002050147600046700013346 0ustar00label.fs-tag,span.fs-tag{background:#ffba00;border-radius:3px;color:#fff;display:inline-block;font-size:11px;line-height:11px;padding:5px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.fs-switch-label{font-size:20px;line-height:31px;margin:0 5px}#fs_log_book table{font-family:Consolas,Monaco,monospace;font-size:12px}#fs_log_book table th{color:#ccc}#fs_log_book table tr{background:#232525}#fs_log_book table tr.alternate{background:#2b2b2b}#fs_log_book table tr td.fs-col--logger{color:#5a7435}#fs_log_book table tr td.fs-col--type{color:#ffc861}#fs_log_book table tr td.fs-col--function{color:#a7b7b1;font-weight:700}#fs_log_book table tr td.fs-col--message,#fs_log_book table tr td.fs-col--message a{color:#9a73ac!important}#fs_log_book table tr td.fs-col--file{color:#d07922}#fs_log_book table tr td.fs-col--timestamp{color:#6596be}freemius/assets/css/admin/connect.css000064400000024462147600046700013724 0ustar00#fs_connect{margin:60px auto 20px;width:484px}#fs_connect a{color:inherit}#fs_connect a:not(.button){text-decoration:underline}#fs_connect .fs-box-container{background:#f0f0f1;border-radius:3px;box-shadow:0 1px 2px rgba(0,0,0,.3);overflow:hidden;padding-top:40px}@media screen and (max-width:483px){#fs_connect{margin:30px 0 0 -10px;width:auto}#fs_connect .fs-box-container{box-shadow:none}}#fs_connect .fs-content{background:#fff;padding:30px 20px}#fs_connect .fs-content .fs-error{background:snow;border:1px solid #d3135a;box-shadow:0 1px 1px 0 rgba(0,0,0,.1);color:#d3135a;margin-bottom:10px;padding:5px;text-align:center}#fs_connect .fs-content h2{line-height:1.5em}#fs_connect .fs-content p{font-size:1.2em;margin:0;padding:0}#fs_connect .fs-license-key-container{margin:10px auto 0;position:relative;width:280px}#fs_connect .fs-license-key-container input{width:100%}#fs_connect .fs-license-key-container .dashicons{position:absolute;right:5px;top:5px}#fs_connect.require-license-key .fs-content{padding-bottom:10px}#fs_connect.require-license-key .fs-actions{border-top:none}#fs_connect.require-license-key .fs-sites-list-container td{cursor:pointer}#fs_connect #delegate_to_site_admins{border-bottom:1px dashed;float:right;font-weight:700;height:26px;line-height:37px;margin-right:15px;text-decoration:none;vertical-align:middle}#fs_connect #delegate_to_site_admins.rtl{margin-left:15px;margin-right:0}#fs_connect .fs-actions{background:#fff;border-color:#f1f1f1;border-style:solid;border-width:1px 0;padding:10px 20px}#fs_connect .fs-actions .button{font-size:16px;height:37px;line-height:35px;margin-bottom:0;padding:0 10px 1px}#fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}#fs_connect .fs-actions .button.button-primary{padding-left:15px;padding-right:15px}#fs_connect .fs-actions .button.button-primary:after{content:" ➜"}#fs_connect .fs-actions .button.button-primary.fs-loading:after{content:""}#fs_connect .fs-actions .button.button-secondary{float:right}#fs_connect.fs-anonymous-disabled .fs-actions .button.button-primary{width:100%}#fs_connect .fs-permissions{background:#fff;padding:10px 20px;transition:background .5s ease}#fs_connect .fs-permissions .fs-license-sync-disclaimer{margin-top:0;text-align:center}#fs_connect .fs-permissions>.fs-trigger{display:block;font-size:.9em;text-align:center;text-decoration:none}#fs_connect .fs-permissions>.fs-trigger .fs-arrow:after{content:"→";display:inline-block;width:20px}#fs_connect .fs-permissions.fs-open>.fs-trigger .fs-arrow:after{content:"↓"!important}#fs_connect .fs-permissions ul li{padding-left:0;padding-right:0}@media screen and (max-width:483px){#fs_connect .fs-permissions ul{height:auto;margin:20px}}#fs_connect .fs-freemium-licensing{background:#777;color:#fff;padding:8px}#fs_connect .fs-freemium-licensing p{display:block;margin:0;padding:0;text-align:center}#fs_connect .fs-freemium-licensing a{color:inherit;text-decoration:underline}#fs_connect .fs-header{height:0;line-height:0;padding:0;position:relative}#fs_connect .fs-header .fs-connect-logo,#fs_connect .fs-header .fs-site-icon{border-radius:50%;position:absolute;top:-8px}#fs_connect .fs-header .fs-site-icon{left:152px}#fs_connect .fs-header .fs-connect-logo{right:152px}#fs_connect .fs-header .fs-site-icon,#fs_connect .fs-header img,#fs_connect .fs-header object{border-radius:50%;height:50px;width:50px}#fs_connect .fs-header .fs-plugin-icon{border-radius:50%;left:50%;margin-left:-44px;overflow:hidden;position:absolute;top:-23px;z-index:1}#fs_connect .fs-header .fs-plugin-icon,#fs_connect .fs-header .fs-plugin-icon img{height:80px;width:80px}#fs_connect .fs-header .dashicons-wordpress-alt{background:#01749a;border-radius:50%;color:#fff;font-size:40px;height:40px;padding:5px;width:40px}#fs_connect .fs-header .dashicons-plus{color:#bbb;font-size:30px;margin-top:-10px;position:absolute;top:50%}#fs_connect .fs-header .dashicons-plus.fs-first{left:28%}#fs_connect .fs-header .dashicons-plus.fs-second{left:65%}#fs_connect .fs-header .fs-connect-logo,#fs_connect .fs-header .fs-plugin-icon,#fs_connect .fs-header .fs-site-icon{background:#fff;border:1px solid #efefef;padding:3px}#fs_connect .fs-terms{font-size:.85em;padding:10px 5px;text-align:center}#fs_connect .fs-terms,#fs_connect .fs-terms a{color:#999}#fs_connect .fs-terms a{text-decoration:none}.fs-multisite-options-container{border:1px solid #ccc;margin-top:20px;padding:5px}.fs-multisite-options-container a{text-decoration:none}.fs-multisite-options-container a:focus{box-shadow:none}.fs-multisite-options-container a.selected{font-weight:700}.fs-multisite-options-container.fs-apply-on-all-sites{border:0;padding:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options{border-spacing:0}.fs-multisite-options-container.fs-apply-on-all-sites .fs-all-sites-options td:not(:first-child){display:none}.fs-multisite-options-container .fs-sites-list-container{display:none;overflow:auto}.fs-multisite-options-container .fs-sites-list-container table td{border-top:1px solid #ccc;padding:4px 2px}#fs_marketing_optin{border:1px solid #ccc;display:none;line-height:1.5em;margin-top:10px;padding:10px}#fs_marketing_optin .fs-message{display:block;font-size:1.05em;font-weight:600;margin-bottom:5px}#fs_marketing_optin.error{background:#fee;border:1px solid #d3135a}#fs_marketing_optin.error .fs-message{color:#d3135a}#fs_marketing_optin .fs-input-container{margin-top:5px}#fs_marketing_optin .fs-input-container label{display:block;margin-top:5px}#fs_marketing_optin .fs-input-container label input{float:left;margin:1px 0 0}#fs_marketing_optin .fs-input-container label:first-child{display:block;margin-bottom:2px}#fs_marketing_optin .fs-input-label{display:block;margin-left:20px}#fs_marketing_optin .fs-input-label .underlined{text-decoration:underline}.rtl #fs_marketing_optin .fs-input-container label input{float:right}.rtl #fs_marketing_optin .fs-input-label{margin-left:0;margin-right:20px}.rtl #fs_connect{border-radius:3px}.rtl #fs_connect .fs-actions{background:#c0c7ca;padding:10px 20px}.rtl #fs_connect .fs-actions .button .dashicons{font-size:37px;margin-left:-8px;margin-right:12px}.rtl #fs_connect .fs-actions .button.button-primary:after{content:" »"}.rtl #fs_connect .fs-actions .button.button-primary.fs-loading:after{content:""}.rtl #fs_connect .fs-actions .button.button-secondary{float:left}.rtl #fs_connect .fs-header .fs-site-icon{left:auto;right:20px}.rtl #fs_connect .fs-header .fs-connect-logo{left:20px;right:auto}.rtl #fs_connect .fs-permissions>.fs-trigger .fs-arrow:after{content:"←"}#fs_theme_connect_wrapper{background:rgba(0,0,0,.75);height:100%;overflow-y:auto;position:fixed;text-align:center;top:0;width:100%;z-index:99990}#fs_theme_connect_wrapper:before{content:"";display:inline-block;height:100%;vertical-align:middle}#fs_theme_connect_wrapper>button.close{background-color:transparent;border:0;color:#fff;cursor:pointer;height:40px;position:absolute;right:0;top:32px;width:40px}#fs_theme_connect_wrapper #fs_connect{display:inline-block;margin-bottom:20px;margin-top:0;text-align:left;top:0;vertical-align:middle}#fs_theme_connect_wrapper #fs_connect .fs-terms,#fs_theme_connect_wrapper #fs_connect .fs-terms a{color:#c5c5c5}.wp-pointer-content #fs_connect{box-shadow:none;margin:0}.fs-opt-in-pointer .wp-pointer-content{padding:0}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow{border-bottom-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-top .wp-pointer-arrow-inner{border-bottom-color:#fafafa}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow{border-top-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-bottom .wp-pointer-arrow-inner{border-top-color:#fafafa}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow{border-right-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-left .wp-pointer-arrow-inner{border-right-color:#fafafa}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow{border-left-color:#dfdfdf}.fs-opt-in-pointer.wp-pointer-right .wp-pointer-arrow-inner{border-left-color:#fafafa}#license_issues_link{display:block;font-size:.9em;margin-top:10px;text-align:center}.fs-tooltip-trigger{position:relative}.fs-tooltip-trigger:not(a){cursor:help}.fs-tooltip-trigger .dashicons{float:none!important}.fs-tooltip-trigger .fs-tooltip{background:rgba(0,0,0,.8);border-radius:5px;bottom:100%;box-shadow:1px 1px 1px rgba(0,0,0,.2);color:#fff!important;font-family:arial,serif;font-size:12px;font-weight:700;left:-17px;line-height:1.3em;margin-bottom:5px;opacity:0;padding:10px;position:absolute;right:0;text-align:left;text-transform:none!important;transition:opacity .3s ease-in-out;visibility:hidden;z-index:999999}.rtl .fs-tooltip-trigger .fs-tooltip{left:auto;right:-17px;text-align:right}.fs-tooltip-trigger .fs-tooltip:after{border-color:rgba(0,0,0,.8) transparent transparent;border-style:solid;border-width:5px 5px 0;content:" ";display:block;height:0;left:21px;position:absolute;top:100%;width:0}.rtl .fs-tooltip-trigger .fs-tooltip:after{left:auto;right:21px}.fs-tooltip-trigger:hover .fs-tooltip{opacity:1;visibility:visible}.fs-permissions .fs-permission.fs-disabled,.fs-permissions .fs-permission.fs-disabled .fs-permission-description span{color:#aaa}.fs-permissions .fs-permission .fs-switch-feedback{position:absolute;right:15px;top:52px}.fs-permissions ul{height:0;margin:0;overflow:hidden}.fs-permissions ul li{margin:0;padding:17px 15px;position:relative}.fs-permissions ul li>i.dashicons{float:left;font-size:30px;height:30px;padding:5px;width:30px}.fs-permissions ul li .fs-switch{float:right}.fs-permissions ul li .fs-permission-description{margin-left:55px}.fs-permissions ul li .fs-permission-description span{color:#23282d;font-size:14px;font-weight:500}.fs-permissions ul li .fs-permission-description .fs-tooltip{font-size:13px;font-weight:700}.fs-permissions ul li .fs-permission-description .fs-tooltip-trigger .dashicons{margin:-1px 2px 0}.fs-permissions ul li .fs-permission-description p{margin:2px 0 0}.fs-permissions.fs-open{background:#fff}.fs-permissions.fs-open ul{height:auto;margin:20px 0 10px;overflow:initial}.fs-permissions .fs-switch-feedback .fs-ajax-spinner{margin-right:10px}.fs-permissions .fs-switch-feedback.success{color:#71ae00}.rtl .fs-permissions .fs-switch-feedback{left:15px;right:auto}.rtl .fs-permissions .fs-switch-feedback .fs-ajax-spinner{margin-left:10px;margin-right:0}.rtl .fs-permissions ul li .fs-permission-description{margin-left:0;margin-right:55px}.rtl .fs-permissions ul li .fs-switch{float:left}.rtl .fs-permissions ul li i.dashicons{float:right}freemius/assets/css/admin/common.css000064400000011545147600046700013561 0ustar00.fs-badge{background:#71ae00;border-radius:3px 0 0 3px;border-right:0;box-shadow:0 2px 1px -1px rgba(0,0,0,.3);color:#fff;font-weight:700;padding:5px 10px;position:absolute;right:0;text-transform:uppercase;top:10px}.theme-browser .theme .fs-premium-theme-badge-container{position:absolute;right:0;top:0}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge{margin-top:10px;position:relative;text-align:center;top:0}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-premium-theme-badge{font-size:1.1em}.theme-browser .theme .fs-premium-theme-badge-container .fs-badge.fs-beta-theme-badge{background:#00a0d2}.fs-switch{background:#ececec;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);box-shadow:0 0 4px rgba(0,0,0,.1),inset 0 1px 3px 0 rgba(0,0,0,.1);color:#ccc;cursor:pointer;display:inline-block;height:18px;padding:6px 6px 5px;position:relative;text-shadow:0 1px 1px hsla(0,0%,100%,.8)}.fs-switch span{display:inline-block;text-transform:uppercase;width:35px}.fs-switch .fs-toggle{background-color:#fff;background-image:linear-gradient(180deg,#ececec,#fff);border:1px solid rgba(0,0,0,.3);border-radius:4px;box-shadow:inset 0 1px 0 0 hsla(0,0%,100%,.5);height:25px;position:absolute;top:1px;transition:.4s cubic-bezier(.54,1.6,.5,1);width:37px;z-index:999}.fs-switch.fs-off .fs-toggle{left:2%}.fs-switch.fs-on .fs-toggle{left:54%}.fs-switch.fs-round{border-radius:24px;padding:4px 25px;top:8px}.fs-switch.fs-round .fs-toggle{border-radius:24px;height:24px;top:0;width:24px}.fs-switch.fs-round.fs-off .fs-toggle{left:-1px}.fs-switch.fs-round.fs-on{background:#0085ba}.fs-switch.fs-round.fs-on .fs-toggle{left:25px}.fs-switch.fs-small.fs-round{padding:1px 19px}.fs-switch.fs-small.fs-round .fs-toggle{border-radius:18px;height:18px;top:0;width:18px}.fs-switch.fs-small.fs-round.fs-on .fs-toggle{left:19px}body.fs-loading,body.fs-loading *{cursor:wait!important}#fs_frame{font-size:0;line-height:0}.fs-full-size-wrapper{margin:40px 0 -65px -20px}@media(max-width:600px){.fs-full-size-wrapper{margin:0 0 -65px -10px}}.fs-notice{position:relative}.fs-notice.fs-has-title{margin-bottom:30px!important}.fs-notice.success{color:green}.fs-notice.promotion{background-color:#f2fcff!important;border-color:#00a0d2!important}.fs-notice .fs-notice-body{margin:.5em 0;padding:2px}.fs-notice .fs-close{color:#aaa;cursor:pointer;float:right}.fs-notice .fs-close:hover{color:#666}.fs-notice .fs-close>*{display:inline-block;margin-top:7px}.fs-notice label.fs-plugin-title{background:rgba(0,0,0,.3);border-radius:0 0 3px 3px;bottom:auto;color:#fff;cursor:auto;font-size:12px;font-weight:700;left:10px;padding:2px 10px;position:absolute;right:auto;top:100%}div.fs-notice.promotion,div.fs-notice.success,div.fs-notice.updated{display:block!important}#fs_connect .fs-error .fs-api-request-error-details,#fs_connect .fs-error .fs-api-request-error-show-details-link,#fs_connect .fs-error ol,.fs-modal .notice-error .fs-api-request-error-details,.fs-modal .notice-error .fs-api-request-error-show-details-link,.fs-modal .notice-error ol,.fs-notice.error .fs-api-request-error-details,.fs-notice.error .fs-api-request-error-show-details-link,.fs-notice.error ol{text-align:left}#fs_connect .fs-error ol,.fs-modal .notice-error ol,.fs-notice.error ol{list-style-type:disc}#fs_connect .fs-error .fs-api-request-error-show-details-link,.fs-modal .notice-error .fs-api-request-error-show-details-link,.fs-notice.error .fs-api-request-error-show-details-link{box-shadow:none;color:#2271b1;text-decoration:none}#fs_connect .fs-error .fs-api-request-error-details,.fs-modal .notice-error .fs-api-request-error-details,.fs-notice.error .fs-api-request-error-details{border:1px solid #ccc;max-height:150px;overflow:auto;padding:5px}.rtl .fs-notice .fs-close{float:left}.fs-secure-notice{background:#ebfdeb;box-shadow:0 2px 2px rgba(6,113,6,.3);color:green;left:160px;opacity:.95;padding:10px 20px;position:fixed;right:0;top:32px;z-index:9989}.fs-secure-notice:hover{opacity:1}.fs-secure-notice a.fs-security-proof{color:green;text-decoration:none}@media screen and (max-width:960px){.fs-secure-notice{left:36px}}@media screen and (max-width:600px){.fs-secure-notice{display:none}}@media screen and (max-width:1250px){#fs_promo_tab{display:none}}@media screen and (max-width:782px){.fs-secure-notice{left:0;text-align:center;top:46px}}span.fs-submenu-item.fs-sub:before{content:"↳";padding:0 5px}.rtl span.fs-submenu-item.fs-sub:before{content:"↲"}.fs-submenu-item.pricing.upgrade-mode{color:#adff2f}.fs-submenu-item.pricing.trial-mode{color:#83e2ff}#adminmenu .update-plugins.fs-trial{background-color:#00b9eb}.fs-ajax-spinner{background:url(/wp-admin/images/wpspin_light-2x.gif);background-size:contain;border:0;display:inline-block;height:20px;margin-bottom:-2px;margin-right:5px;vertical-align:sub;width:20px}.wrap.fs-section h2{text-align:left}.plugins p.fs-upgrade-notice{background-color:#d54e21;border:0;color:#f9f9f9;margin-top:10px;padding:10px}freemius/assets/css/admin/clone-resolution.css000064400000004001147600046700015557 0ustar00.fs-notice[data-id^=clone_resolution_options_notice]{color:inherit!important;padding:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-notice-body{margin-bottom:0;padding:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-notice-header{padding:5px 10px}.fs-notice[data-id^=clone_resolution_options_notice] ol{margin-bottom:0;margin-top:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-options-container{display:flex;flex-direction:row;padding:0 10px 10px}@media(max-width:750px){.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-options-container{flex-direction:column}}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option{border:1px solid #ccc;flex:auto;margin:5px;padding:10px 10px 15px}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option:first-child{margin-left:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option:last-child{margin-right:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-resolution-option strong{font-size:1.2em;line-height:1.5em;padding:2px}.fs-notice[data-id^=clone_resolution_options_notice] a{text-decoration:none}.fs-notice[data-id^=clone_resolution_options_notice] .button{margin-right:10px}.rtl .fs-notice[data-id^=clone_resolution_options_notice] .button{margin-left:10px;margin-right:0}.fs-notice[data-id^=clone_resolution_options_notice] .fs-clone-documentation-container{padding:0 10px 15px}.fs-notice[data-id=temporary_duplicate_notice] #fs_clone_resolution_error_message{background:#fee;border:1px solid #d3135a;color:#d3135a;padding:10px}.fs-notice[data-id=temporary_duplicate_notice] ol{margin-top:0}.fs-notice[data-id=temporary_duplicate_notice] a{position:relative}.fs-notice[data-id=temporary_duplicate_notice] a:focus{box-shadow:none}.fs-notice[data-id=temporary_duplicate_notice] a.disabled{color:gray}.fs-notice[data-id=temporary_duplicate_notice] a .fs-ajax-spinner{bottom:0;left:8px;margin-left:100%;position:absolute;right:0;top:-1px}freemius/assets/css/admin/checkout.css000064400000002151147600046700014067 0ustar00.fs-ajax-loader{height:20px;margin:auto;position:relative;width:170px}.fs-ajax-loader .fs-ajax-loader-bar{animation-direction:normal;animation-duration:1.5s;animation-iteration-count:infinite;animation-name:bounce_ajaxLoader;background-color:#fff;height:20px;position:absolute;top:0;transform:scale(.3);width:20px}.fs-ajax-loader .fs-ajax-loader-bar-1{animation-delay:.6s;left:0}.fs-ajax-loader .fs-ajax-loader-bar-2{animation-delay:.75s;left:19px}.fs-ajax-loader .fs-ajax-loader-bar-3{animation-delay:.9s;left:38px}.fs-ajax-loader .fs-ajax-loader-bar-4{animation-delay:1.05s;left:57px}.fs-ajax-loader .fs-ajax-loader-bar-5{animation-delay:1.2s;left:76px}.fs-ajax-loader .fs-ajax-loader-bar-6{animation-delay:1.35s;left:95px}.fs-ajax-loader .fs-ajax-loader-bar-7{animation-delay:1.5s;left:114px}.fs-ajax-loader .fs-ajax-loader-bar-8{animation-delay:1.65s;left:133px}@keyframes bounce_ajaxLoader{0%{background-color:#0074a3;transform:scale(1)}to{background-color:#fff;transform:scale(.3)}}@media screen and (max-width:782px){#wpbody-content{padding-bottom:0!important}}.fs-checkout-process-redirect{padding:40px;text-align:center}freemius/assets/css/admin/affiliation.css000064400000004134147600046700014552 0ustar00#fs_affiliation_content_wrapper #messages{margin-top:25px}#fs_affiliation_content_wrapper h3{font-size:24px;margin-left:0;padding:0}#fs_affiliation_content_wrapper ul li{box-sizing:border-box;list-style-type:none}#fs_affiliation_content_wrapper ul li:before{content:"✓";font-weight:700;margin-right:10px}#fs_affiliation_content_wrapper label,#fs_affiliation_content_wrapper li,#fs_affiliation_content_wrapper p:not(.description){font-size:16px!important;line-height:26px!important}#fs_affiliation_content_wrapper .button{font-size:16px;height:40px;line-height:35px;margin-bottom:7px;margin-top:20px}#fs_affiliation_content_wrapper .button#cancel_button{margin-right:5px}#fs_affiliation_content_wrapper form .input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form .input-container .input-label{display:block;font-weight:700;width:100%}#fs_affiliation_content_wrapper form .input-container.input-container-text input,#fs_affiliation_content_wrapper form .input-container.input-container-text label,#fs_affiliation_content_wrapper form .input-container.input-container-text textarea{display:block}#fs_affiliation_content_wrapper form .input-container #add_domain,#fs_affiliation_content_wrapper form .input-container .remove-domain{display:inline-block;margin-top:3px;text-decoration:none}#fs_affiliation_content_wrapper form .input-container #add_domain:focus,#fs_affiliation_content_wrapper form .input-container .remove-domain:focus{box-shadow:none}#fs_affiliation_content_wrapper form .input-container #add_domain.disabled,#fs_affiliation_content_wrapper form .input-container .remove-domain.disabled{color:#aaa;cursor:default}#fs_affiliation_content_wrapper form #extra_domains_container .description{margin-top:0;position:relative;top:-4px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container{margin-bottom:15px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain{display:inline-block;margin-right:5px}#fs_affiliation_content_wrapper form #extra_domains_container .extra-domain-input-container .domain:last-of-type{margin-bottom:0}freemius/assets/css/admin/add-ons.css000064400000025721147600046700013617 0ustar00.fs-badge{background:#71ae00;border-radius:3px 0 0 3px;border-right:0;box-shadow:0 2px 1px -1px rgba(0,0,0,.3);color:#fff;font-weight:700;padding:5px 10px;position:absolute;right:0;text-transform:uppercase;top:10px}#fs_addons .fs-cards-list{list-style:none}#fs_addons .fs-cards-list .fs-card{border:1px solid #ddd;cursor:pointer;float:left;font-size:14px;height:152px;list-style:none;margin:0 0 30px 30px;padding:0;position:relative;width:310px}#fs_addons .fs-cards-list .fs-card .fs-overlay{bottom:0;left:0;position:absolute;right:0;top:0;z-index:9}#fs_addons .fs-cards-list .fs-card .fs-inner{background-color:#fff;height:100%;overflow:hidden;position:relative}#fs_addons .fs-cards-list .fs-card .fs-inner>ul{left:0;position:absolute;right:0;top:0;transition:all,.15s}#fs_addons .fs-cards-list .fs-card .fs-inner>ul>li{box-sizing:border-box;display:block;line-height:18px;list-style:none;padding:0 15px;width:100%}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner{background-repeat:repeat-x;background-size:100% 100%;display:block;height:100px;line-height:0;margin:0;padding:0;transition:all,.15s}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-card-banner .fs-badge.fs-installed-addon-badge{font-size:1.02em;line-height:1.3em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-title{color:#000;font-weight:700;height:18px;margin:10px 0 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-offer{font-size:.9em}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-description{background-color:#f9f9f9;border-top:1px solid #eee;color:#777;margin:0 0 10px;padding:10px 15px 100px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-tag{background:#adff2f;box-shadow:1px 1px 1px rgba(0,0,0,.3);display:block;font-size:.9em;font-weight:700;padding:2px 10px;position:absolute;right:0;text-transform:uppercase;top:10px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button,#fs_addons .fs-cards-list .fs-card .fs-inner .fs-cta .button-group{position:absolute;right:10px;top:112px}@media screen and (min-width:960px){#fs_addons .fs-cards-list .fs-card:hover .fs-overlay{border:2px solid #29abe1;margin-left:-1px;margin-top:-1px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner ul{top:-100px}#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-offer,#fs_addons .fs-cards-list .fs-card:hover .fs-inner .fs-title{color:#29abe1}}#TB_window,#TB_window iframe{width:821px!important}#plugin-information .fyi{width:266px!important}#plugin-information #section-holder{clear:none;margin-right:299px}#plugin-information #section-description b,#plugin-information #section-description blockquote,#plugin-information #section-description h2,#plugin-information #section-description h3,#plugin-information #section-description i,#plugin-information #section-description li,#plugin-information #section-description ol,#plugin-information #section-description p,#plugin-information #section-description ul{clear:none}#plugin-information #section-description iframe{max-width:100%}#plugin-information #section-description .fs-selling-points{border-bottom:1px solid #ddd;padding-bottom:10px}#plugin-information #section-description .fs-selling-points ul{margin:0}#plugin-information #section-description .fs-selling-points ul li{list-style:none outside none;padding:0}#plugin-information #section-description .fs-selling-points ul li i.dashicons{color:#71ae00;float:left;font-size:3em;line-height:30px;margin:0 0 0 -15px;vertical-align:middle}#plugin-information #section-description .fs-selling-points ul li h3{margin:1em 30px!important}#plugin-information #section-description .fs-screenshots:after{clear:both;content:"";display:table}#plugin-information #section-description .fs-screenshots ul{list-style:none;margin:0}#plugin-information #section-description .fs-screenshots ul li{box-sizing:content-box;float:left;height:225px;margin-bottom:20px;width:225px}#plugin-information #section-description .fs-screenshots ul li a{background-size:cover;border:1px solid;box-shadow:1px 1px 1px rgba(0,0,0,.2);display:block;height:100%;width:100%}#plugin-information #section-description .fs-screenshots ul li.odd{margin-right:20px}#plugin-information .plugin-information-pricing{border-bottom:1px solid #ddd;margin:-16px}#plugin-information .plugin-information-pricing .fs-plan h3{font-size:16px;margin-top:0;padding:20px}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper{border-bottom:1px solid #ddd}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab{cursor:pointer;font-size:.9em;padding:0 10px;position:relative}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab label{background:#adff2f;border:1px solid #006400;bottom:100%;color:green;font-size:.9em;left:-1px;line-height:1em;padding:2px;position:absolute;right:-1px;text-align:center;text-transform:uppercase}#plugin-information .plugin-information-pricing .fs-plan .nav-tab-wrapper .nav-tab.nav-tab-active{background:#fffeec;border-bottom-color:#fffeec;cursor:default}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle h3{background:#fffeec;color:#0073aa;margin:0;padding-bottom:0}#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .fs-billing-frequency,#plugin-information .plugin-information-pricing .fs-plan.fs-single-cycle .nav-tab-wrapper{display:none}#plugin-information .plugin-information-pricing .fs-plan .fs-pricing-body{background:#fffeec;padding:20px}#plugin-information .plugin-information-pricing .fs-plan .button{font-size:1.1em;font-weight:700;text-align:center;text-transform:uppercase;width:100%}#plugin-information .plugin-information-pricing .fs-plan label{white-space:nowrap}#plugin-information .plugin-information-pricing .fs-plan var{font-style:normal}#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount,#plugin-information .plugin-information-pricing .fs-plan .fs-billing-frequency{background:#f3f3f3;border:1px solid #ccc;display:block;font-weight:700;margin-bottom:10px;padding:2px;text-align:center;text-transform:uppercase}#plugin-information .plugin-information-pricing .fs-plan .fs-annual-discount{background:#adff2f;color:green;text-transform:none}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms{font-size:.9em}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms i{float:left;margin:0 0 0 -15px}#plugin-information .plugin-information-pricing .fs-plan ul.fs-trial-terms li{margin:10px 0 0}#plugin-information #section-features .fs-features{margin:-20px -26px}#plugin-information #section-features table{border-collapse:separate;border-spacing:0;width:100%}#plugin-information #section-features table thead th{padding:10px 0}#plugin-information #section-features table thead .fs-price{color:#71ae00;display:block;font-weight:400;text-align:center}#plugin-information #section-features table tbody td{border-top:1px solid #ccc;color:#71ae00;padding:10px 0;text-align:center;width:100px}#plugin-information #section-features table tbody td:first-child{color:inherit;padding-left:26px;text-align:left;width:auto}#plugin-information #section-features table tbody tr.fs-odd td{background:#fefefe}#plugin-information #section-features .dashicons-yes{font-size:30px;height:30px;width:30px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button,#plugin-information .fs-dropdown .button-group .button{position:relative;right:0;top:0;width:auto}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .button:focus,#plugin-information .fs-dropdown .button-group .button:focus{z-index:10}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .button-group .fs-dropdown-arrow,#plugin-information .fs-dropdown .button-group .fs-dropdown-arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #fff;position:relative;top:12px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active:not(.up) .button:not(.fs-dropdown-arrow-button){border-bottom-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active:not(.up) .fs-dropdown-arrow-button{border-bottom-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button),#plugin-information .fs-dropdown.active.up .button:not(.fs-dropdown-arrow-button){border-top-left-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.active.up .fs-dropdown-arrow-button,#plugin-information .fs-dropdown.active.up .fs-dropdown-arrow-button{border-top-right-radius:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list,#plugin-information .fs-dropdown .fs-dropdown-list{background-color:#fff;border:1px solid #bfbfbf;box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12);margin-left:auto;padding:3px 0;position:absolute;right:-1px;text-align:left;top:100%;width:230px;z-index:1}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li,#plugin-information .fs-dropdown .fs-dropdown-list li{margin:0}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li a,#plugin-information .fs-dropdown .fs-dropdown-list li a{display:block;padding:5px 10px;text-decoration:none;text-shadow:none}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover,#plugin-information .fs-dropdown .fs-dropdown-list li:hover{background-color:#0074a3;color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown .fs-dropdown-list li:hover a,#plugin-information .fs-dropdown .fs-dropdown-list li:hover a{color:#fff}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown:not(.up) .fs-dropdown-list,#plugin-information .fs-dropdown:not(.up) .fs-dropdown-list{border-radius:3px 0 3px 3px}#fs_addons .fs-cards-list .fs-card .fs-inner .fs-dropdown.up .fs-dropdown-list,#plugin-information .fs-dropdown.up .fs-dropdown-list{border-radius:3px 3px 0 3px}#plugin-information .fs-dropdown .button-group{width:100%}#plugin-information .fs-dropdown .button-group .button{float:none;font-size:14px;font-weight:400;text-transform:none}#plugin-information .fs-dropdown .fs-dropdown-list{margin-top:1px}#plugin-information .fs-dropdown.up .fs-dropdown-list{bottom:100%;margin-bottom:2px;top:auto}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group{display:table;text-align:center}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button{display:table-cell}#plugin-information.wp-core-ui .fs-pricing-body .fs-dropdown .button-group .button:not(.fs-dropdown-arrow-button){left:1px;width:100%}#plugin-information-footer .fs-dropdown,#plugin-information-footer>.button{position:relative;top:3px}#plugin-information-footer .fs-dropdown.left,#plugin-information-footer>.button.left{float:left}#plugin-information-footer .fs-dropdown,#plugin-information-footer>.right{float:right}@media screen and (max-width:961px){#fs_addons .fs-cards-list .fs-card{height:265px}}freemius/assets/css/admin/account.css000064400000012306147600046700013721 0ustar00label.fs-tag,span.fs-tag{background:#ffba00;border-radius:3px;color:#fff;display:inline-block;font-size:11px;line-height:11px;padding:5px;vertical-align:baseline}label.fs-tag.fs-warn,span.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-info,span.fs-tag.fs-info{background:#00a0d2}label.fs-tag.fs-success,span.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error,span.fs-tag.fs-error{background:#dc3232}.fs-notice[data-id=license_not_whitelabeled].success,.fs-notice[data-id=license_whitelabeled].success{border-left-color:#00a0d2;color:inherit}.fs-notice[data-id=license_not_whitelabeled].success label.fs-plugin-title,.fs-notice[data-id=license_whitelabeled].success label.fs-plugin-title{display:none}#fs_account .postbox,#fs_account .widefat{max-width:800px}#fs_account h3{border-bottom:1px solid #f1f1f1;font-size:1.3em;line-height:1.4;margin:0 0 12px;padding:12px 15px}#fs_account h3 .dashicons{font-size:1.3em;height:26px;width:26px}#fs_account i.dashicons{font-size:1.2em;height:1.2em;width:1.2em}#fs_account .dashicons{vertical-align:middle}#fs_account .fs-header-actions{font-size:.9em;position:absolute;right:15px;top:17px}#fs_account .fs-header-actions ul{margin:0}#fs_account .fs-header-actions li{float:left}#fs_account .fs-header-actions li form{display:inline-block}#fs_account .fs-header-actions li a{text-decoration:none}#fs_account_details .button-group{float:right}.rtl #fs_account .fs-header-actions{left:15px;right:auto}.fs-key-value-table{width:100%}.fs-key-value-table form{display:inline-block}.fs-key-value-table tr td:first-child{text-align:right}.fs-key-value-table tr td:first-child nobr{font-weight:700}.fs-key-value-table tr td:first-child form{display:block}.fs-key-value-table tr td.fs-right{text-align:right}.fs-key-value-table tr.fs-odd{background:#ebebeb}.fs-key-value-table td,.fs-key-value-table th{padding:10px}.fs-key-value-table code{line-height:28px}.fs-key-value-table code,.fs-key-value-table input[type=text],.fs-key-value-table var{background:none;color:#0073aa;font-size:16px}.fs-key-value-table input[type=text]{font-weight:700;width:100%}.fs-field-beta_program label{margin-left:7px}label.fs-tag{border-radius:3px;color:#fff;display:inline-block;font-size:11px;line-height:11px;padding:5px;vertical-align:baseline}label.fs-tag,label.fs-tag.fs-warn{background:#ffba00}label.fs-tag.fs-success{background:#46b450}label.fs-tag.fs-error{background:#dc3232}#fs_sites .fs-scrollable-table .fs-table-body{border:1px solid #e5e5e5;max-height:200px;overflow:auto}#fs_sites .fs-scrollable-table .fs-table-body>table.widefat{border:none!important}#fs_sites .fs-scrollable-table .fs-main-column{width:100%}#fs_sites .fs-scrollable-table .fs-site-details td:first-of-type{color:gray;text-align:right;width:1px}#fs_sites .fs-scrollable-table .fs-site-details td:last-of-type{text-align:right}#fs_sites .fs-scrollable-table .fs-install-details table tr td{white-space:nowrap;width:1px}#fs_sites .fs-scrollable-table .fs-install-details table tr td:last-of-type{width:auto}#fs_addons h3{border:none;margin-bottom:0;padding:4px 5px}#fs_addons td{vertical-align:middle}#fs_addons thead{white-space:nowrap}#fs_addons td:first-child,#fs_addons th:first-child{font-weight:700;text-align:left}#fs_addons td:last-child,#fs_addons th:last-child{text-align:right}#fs_addons th{font-weight:700}#fs_billing_address{width:100%}#fs_billing_address tr td{padding:5px;width:50%}#fs_billing_address tr:first-of-type td{padding-top:0}#fs_billing_address span{font-weight:700}#fs_billing_address input,#fs_billing_address select{display:block;margin-top:5px;width:100%}#fs_billing_address input::-moz-placeholder,#fs_billing_address select::-moz-placeholder{color:transparent}#fs_billing_address input::placeholder,#fs_billing_address select::placeholder{color:transparent}#fs_billing_address input.fs-read-mode,#fs_billing_address select.fs-read-mode{background:none;border-color:transparent;border-bottom:1px dashed #ccc;color:#777;padding-left:0}#fs_billing_address.fs-read-mode td span{display:none}#fs_billing_address.fs-read-mode input,#fs_billing_address.fs-read-mode select{background:none;border-color:transparent;border-bottom:1px dashed #ccc;color:#777;padding-left:0}#fs_billing_address.fs-read-mode input::-moz-placeholder,#fs_billing_address.fs-read-mode select::-moz-placeholder{color:#ccc}#fs_billing_address.fs-read-mode input::placeholder,#fs_billing_address.fs-read-mode select::placeholder{color:#ccc}#fs_billing_address button{display:block;width:100%}@media screen and (max-width:639px){#fs_account .fs-header-actions{margin:0 0 12px;padding:0 15px 12px;position:static}#fs_account .fs-header-actions li{display:inline-block;float:none}#fs_account #fs_account_details,#fs_account #fs_account_details tbody,#fs_account #fs_account_details td,#fs_account #fs_account_details th,#fs_account #fs_account_details tr{display:block}#fs_account #fs_account_details tr td:first-child{text-align:left}#fs_account #fs_account_details tr td:nth-child(2){padding:0 12px}#fs_account #fs_account_details tr td:nth-child(2) code{margin:0;padding:0}#fs_account #fs_account_details tr td:nth-child(2) label{margin-left:0}#fs_account #fs_account_details tr td:nth-child(3){text-align:left}#fs_account #fs_account_details tr.fs-field-plan td:nth-child(2) .button-group{float:none;margin:12px 0}}freemius/assets/css/index.php000064400000000127147600046700012301 0ustar00_8b>a'QIDATx^]cc9ױmmf۶m۶m۶Ƕ>y3$TSgM:>xшF4hD#шF4hD#шF4hD#шF4chH Brrۃglmm/h_TD^AꝸKO9v*19dŊU4VʪL|nԟ)|aI'>.ִ_֖z/q/7;f Q la\GAm 4)f2ؿ{atTm8ؕ A<'3g9 2hRU~qU(۔r sH&.gYJ28X-4a ,YfL 4S%: 1ɡ`PR81Ǐ{2JE #8Zƚ53Ne5ox`u?LswGuߕir  0f ?ѵT>#IF$/9p@)^k?wvirR:c.e~)zLBZR 2>7 2"v19aI^oHiO+luWA{.,FnɾnEqGc1IYo-(Dm[ʲx\7@)OӚhՖ lI+7.'Z Dwj o+}n-fR1|AP^T&?P*[|t=du﫦3>! c-&>fKsZ-ͪqi!`J@Oh2A}Z=֊=/Is[$(&mrnL$7Rsy-235/qH I" 'iuD 9\_))!Cd"UI_6h [N2Yd||ْ,`a?x h/P9VI!B -'?G|<>AgPGNrEĢu ucmѧOz3o\Wam:hlKU{GlB[`P02Ȥb@߻3GI8q {w"E 6! Ĝn2O!Jv3*TF'B GaS88'FBTn]jt–Rs2pӧ(k KO5t FqA] H/ZiUnbBw߬2JP)O18H0vcYG!B̴5ۊ)MSaAG*(VzmV E~Ɣ"w[!0ᒁ QVX2sgokqن7;Q@ڼshB_;MKL e%GkHz`E掏DHʨedtmtF KPC^.BX\T|Y Q,)2C$z%J"*Ҫ}XT aY+"`M\?Nfz r"k,KB>F|ukױ?uk3WKJ s#9~/o63xТH;j'!P9|f^\(t'㞚Ѩ)NYa(+K!>nER#ۜvEE~9PB2p^Y>n/Ԗ׌W@U\S T}8, *2_ dwFsmj-9e(q*L=#qI`W$P@ղ>H ' v PէTL4ycPPkK",yIʎb.sR|]m63CX^u@y^?HljTqUEP:ΐͫ!*^XݛЪ@)P$a t_} ;+e#(nz?s,3b<^:V9sr)\aidFw v(hᗒ%OY,W,~{!;jK\Ԕ; Gj/9iCXn^q5l3mEGVNxt'%K+#Pt|nk*269C瀳<ɒIjyYgC瑤K}i~D~oe[KlfF2ϛ ]չ42("%;b.v*0;3I+ бkl5tx~e9@qSIݟJׇksx[8%/<^gYM3X=c]K .h4LqdsT@KZbPŨ\`@3I9|<q7܃A:p,!9r F-7;k?G\KVuK1b E3Mnh3_;yc:&Ӹ!ãgJ]jjX 5+Yjб_:uUױ5†Bc>Q(z oq}кD2W$U, ]46.UL2N]w[~7jQwn]i-!ir'S![Swlf^YZ<+<Au/@WS0}E < "KMCi:rvoY_%{'(h80W"1)}+ïe]L#whD|7]{?6U M@ҮW#섵u!̫_nql.w@ f%'b#p 0JUZTE f(c}AbY"UZ§y_ZS7XM֒oht?Â,^JNL<( ұͳPN(HL͇iQ . zҪ+-@U&XTJ̒7m<]`hz $ `W#7+.  :Xy`w&ҦVo3+ںvxތ[1j[@5 *  g*cs *u%ߨ8"nK]|pT۸Bu \y,y~#Zf&)Wʯ[9";9.}cHFM~mm:@ %WL?4Y톿 (+uG -4]bcTZNȜ%drpx·=gAcզMSG[@2-"Ow>IkR̙8nz9 }I5H[^s–o7_m@<8ESJ N><خ[ B);aLݿZ5ʀ JX Jz )ϭ-@YUO妋xakl0@_:@&4S\ܢ+r!%"$]wϬz̷eljTn2kS "^WqfE qˢA)m,x Yċ e+ aۦaxIJ˺Dkx *I+d|^۳rU,᷿$=Dړ^XN7CޑnLy\a>5SJv/Xw@Cw?f6E{lƭ >{}6źlvxWIǖti DVkM0Q@iڷ6ЉE,#}-f .Pvg]_%4tK5B3L(,%}ų[!^ҏC q̈́R㸲qv_•jjcKChzx;GJQyq+;դ82}-ޅh4vgR;XTC[9.BJ+PցK}_,Iͬ s8`Y uBCW1(~2W@!uC^p}fֈTT)W(]m~veo%?J>Eýߠ DE)Ġu6(.٘g}d 0tu]*FEUunO:TiC040B2ix @Q5+ȭ}:ܒhHI˜;4OQﺎK( GTGi;%ŹUOel͂3`u-{s6DaЮd~/v\55>>ϏZRrR،Ö -.(X}e?"J⸕ ]z7FjU:wNen!, -f pc|ۨ( Cu2Xi4mձrtЎ~uJ{~(po8q+P Pe!MWox'2\/aԸ}+f_J8"" !%0<Hǎu<8IsKHOI17d~K2dž9ܪWkpElp^G-6 +LmYGGuR8 R AP]G2e J5|8r>ؤm!ӓޓ|X)\VbgzU塭L0 k΢iG<9/,x|g]\4|y\(jӪ̞[$wK5|m ͮRoA02;@[\B)%`3S*d{)1:zg8] fbG]=v`Qu5k]Bn[5GPn>"3J띬h 颐qzJ`q.-v^s^Ѷr8ڻ?~m=^6DC o8&qmXs(HyD Ա^bBfֽ b2h֫ M0Nyxcj%(M];HP<@[퇊q0SL&R b3 -ePq3cIlQhдOIxN濾RDNK#Ȗ-(MhъOrѲTpq ;qoՉ6uPGtf6#>oKK@U0}QUJ`g@is U.$a@PTÖ<͕@Xw[;E) |n?|gC~c!^c3| WlI4;[9d+EQŮ9Ӝ(+1A ^]#”~3GljZmR_(jeG"vpT~ߗX &ѹ z^BϏg<gW&{\(k-ju6 y'_E(m= e_ȰP\ \9P2襚lKsE'i}2}}1I6g "OPJٗ\FFv B\RU}"QǾdöA5ɈTv%DەOsE{j %vza >C\mm( UKsy>C{`,mΣ wT&Tmqhw7XP`ِK ֤>@<($Shg=0n>*]2)/VzFICqK|+1=T( >"yM1KO,!w5D=Gi iop6魦 jGy EReC4B9%a &(_>Q#EUةv :C<`}Ns7k>{HSHy2( \`Vn X ӓ1Cnw)"! "/GGB̳WT̷z  jt)rk:VK&I$_qf ' @2d2d͐lBb*-?? O\|JM-NJ=/@dYꑅhf([͝Tc>pY@Xɑ&L+ D5M D Yy$,"Pޖ;nxf)d yn*9Gm/tۘBBZ¿@3 #u=s Iuai;}le_X\2=ϻb̫fAdfU{EVi(ޢn8JL@[lCv)we!lCD%:u2^}مGۍz)>Mu73s,-to@[-3K?dM5eDPh@>MhPWӡ],d4=q9W+t8o+lyzR @,;Zr4l[.P]8%6ِBMAClj+9஘RoJ.6-kE_9ѐ7eG9!czk>_,PQw{f9auA ?9r׽?iܩ!5:E%۰,+XOSAPL׿6fxnoZ%crH1+sE|ʛ'ڰXx⺻BR; aP5j% PdX|_oVOAU釶}ڮM >'c:yys5вk7N)[eqp^MC.#ӺFWPFÂPj2>RhEfMw}8jϟw9 fTAK^GZn405jݞUePnlrO;a Ⱦa@>(7"?IǏ]R=\/H\3ihdnM.͐hSv볯ۼ "=p>4R ~GwJcq.v+{[!^d4<)ˢC< ؆!kaa-u0ss 5X-0t~wdWndo; h[Z5Rm~Z7Lnz|w< 6߾ƛ-;̓  iz}kOޏ?@2tM`:6Q; h+nkdmW [OVL! !A)BFCN:|v7fcóBDxq%o "WEMۀ/B4v_DTp_ABWƾX)yg;LV) |a5&k!e?faC۰M h7P#Zrz᧞xzh*@ 13|L9D..Nz'г]ϝ _;tY*=k!ڴ8֬wc[,0볪fP jHa{4& NgVcQEL.M?@۩Yf,T3n`*,;j]m"X@Bo,aTeL@̝R.leXel@4yL) 1v얅ĴW Є3нvkǰ/Mg_4y}.2KW# [g`O FC? گ:hA1B"zM|##/:HjPUw( Qdzo%Xh+ A "`ZZFh XYZZk! R7ݙbx0㴲s? :V:f6klHbJ~;.7Q=\kosJEnϹ~scEU ̫ՠ>u ݅j;fJɣ0Jo{i:}GLϿ)VEq' IENDB`freemius/assets/img/webinar-ignition.png000064400000015200147600046700014416 0ustar00PNG  IHDRPLTEGpLU?3B>806<=<=A>CDC@JN<S=U@X?[@B[d?V@iX?p[>\t`?ZdxJ<dn}cCn?Y@sbuRe?x~T?|gTLi[@MmI]?mLbEsUiaQE}qGcRYKzeWJo\~md_QIsfWMmh[uOndz[RO~nDbV<Xyh^ugZQбEtRNS "$&)*./2489==ABFFIKMPSUVXZ___aegggjmooostwv{|}~>c`/IDATxڼZ]$U=FȬ鞏|5+hؘO H<Bw! " ]=㝝tWtUVefY3t='+RUuN{&j+u1OcCv*ϫPAk:h&f_A]{# zo=SQy1@Pr &z xюo=C T;'R6sq_=j Hq \`gKjcPǯVh=N 9@ֻHX)ͬiТ"O9>EEw0ZV{ÝZ_Gr-^9.>X(INܞFwG1ޅ A`;t $f"ތP4(oAZx~2I"[~J{^"\\qBDZr!ԣ`@߂niF8NSDnO_2du9RKL*8C_\2@ $2ٹ OXW#S-"C`)H"Y/Eg`Gs/XPi)d5\o߾V>6K C҃a>g;+Q o: `n3?A!kE+''all*_l7R/`a\U ݍ"9`kSZNLt1nlm-׮[ ރXDwP0TVϞd-|sЖ^GOJ]rsRGolnq17H+կ`]98k|cx8LG7 у7)WTh깃,lAWA:E{16/+;ljRFxqfׂ0PfڝW3 2&)?70;  Ёe˽?hYDp3)0\ w1CXE<'VZxB^ pn dIFF F9c~#ߡCځuj 1t1 M16 l:)Sv]NBfvLTK&*"y(N'M団,fEߪ BX42;N o<5-ZBN؏e'}flX/g}:~ bb-4aJ/qա]"#ıJSO@g e!Zk#2JL4|.K^NKϼXD 6n!,SPqcrfkjN+?Qo]B_ڃ ݐvݸ"N9A4H\wdQYҭ?Ož}eOy@47/kEiDQZ!QH_LSvDW"%i~U{^.iáR?Pgo!ܒI1e=rPYToh ;>U{u(4w "7`,N&ee C9Y(?Bqvyx2O&P_j \p!rcK'iE+~t`?Khp{kj\͗~?s\po7.'ӛ8uE,rVP?@ yĞl:ߟf{dx8则O[#>E!WB=v}l݊+,.-?Ljx̫r:E!!@/c|IN?\-OQ Gwh*gg<-fU**[Ϩnދ:q~詡A[ IގP/ѤG3UlQϿVpg@ ׷}Cjȹ^eSS]V%m1b`OhgKzu_P4/G̑MxRV?1BfɁBqt_nӆve!b84H$'YCT[Kc^9kO8 in;j m7ūr2FEտT[a>MNڛWu|ׯ㕓TGcVe[?okg؀<y2,*<>DK~ o/j`]}OE\TC@HOV@z88X߫CL;G 1u,Z [F; QGz9\ه.X4P Hh"X8V*"0Df >\/[5ZӲ@+Ppor\`ZyMT" 2䓯(Y-?-.<@ [Rz 8g5ɀa ` ո?y 6F_ (}LۨYD]!$HH3= ,Xg`@W%!R%Jaa`h:wm< 0 (pO-q?iФ]sY"ȱB=+p?P41ʧe8@B(k٣GQ{ނv3y/3TqsPaB-;~(`T`⤈"J}B@9^HV350 9/A9[8$ҽXC<~1Q@/B#>?;]l9XAT p0D'd9 (n< U۹v̻r `\9Z(0(au4 Xiݴh]A"2X3ѹ¾IdNj`9n`s H9rb4MB $Eq슳ƀW3sΎ8vDn*XYb0i񬎎 GRhD!k.՛cCiYi*|:,`!9"Jf5O;@_/a1Nl6:gcO%A2r1`~tż^VM-D~cJѰklYuPo4$ִq2(?I't6vNLJ'9+eQ6N~rݬ,[RF|IgL {6oc%>nA_\u/n1no/ DQ8gteѰB(C92=KU8gn/)N1p AnLdU5 pp`vehmon\ǭIq׼]ԹY_a2l滻ʣYϤR۪ }}Ϗs⺛PM$Ňbv;I$tRNS@f*IDATx^ǎ0P$ԩ f{$<ɱ6K)RJ)RʆlgN US6[D9kş8$r\{zwܑvyqHBt.-2h_ Bn̆ Eɡ٥6#B6md"#R \ݾ;aSڮ\~OӵZ.tnoz<_3.k|T?uf`eތz۶ dVi* ۺ!C'1Ài(aH Gx&cJ'_ZKH;B1N'BN'͏-צS(#pէv@Zea?Z 8j:ggH<=T#x%:袣\%r%qba"n3:*}DM(ѐ W!B΅OFҮ1+pDZ@sOQA}u|m2L!CQT 7Q 5T(H$QRg+*j!G׌ hVVԎ(- Adur ":@*- &+qDBp#EdֶCTQ-7|D1]nsm>YQòqHY Pп'cA#\3dS`q3Kb^c3@ړ@K[- -|`lܳ3X\(IA -rH)kQJ @Vt9 C`wа)VE#h THhj^` a;IZc3H@xr 7'lyn/#HQ?Q ++n68/HYt ЗN5?3w¾sKeHl+]Lǿi#*Fr,*vAz4/rVLDG  Y ̌&`Ki姓9{Ba@@ G@ `"D R-N*r 6)SSX!+V0 G6,Vm# +̟Ѝyek"_{ej6BZ? r)H,zm7;z(n}@7D^&vDD. X4~ # &Rt>泠ت)%Y qP#dC^̣Po`pa/?|r #׀)0!?d]%M買Y@יƚT]+x0J<5×{̓Z2(*[￑+Yv/A!Mp[63-L_c<>e{0tH>- R)€ͯY+W"DɁ; &EO-`Cx^78c=^XÀjßր4#GMVt8I rz6@yhv^S`7l,`* >7X 9K3YLCP=OX|?#A `@-y=}nA!}l22#@ O %E&#a1@ܝW#Zۆ0UءnA)EKvBbrS.ұvw)gռ,G;"=J (3  n(dl;W{n]!TDc8y" @zC5~TC9P`|G`Ma$Y`#r <* @UsP(9DF/C}Uq&4y@Ѡ(h0gΝ wguCVmնm^Hab$\ @_4" eI#UZ?n'}w*Q!~ٯR~Z cP7:#w;u *Xt`Mdep.IRJҨhݏL xdViLbivtn^u: s)͈'mBC@`u}OEJU3j.;$nyߚWV@.Q )&[3vbJ07X=.S T Pcmt!4p{=bW!W`ԶP`!9Kq3d%k6x<$/;%G?y96a0+ĤYZE -LKViѪ;!{^ 6\6DHϔ@_ҷO6zxꫫR Z-X{QoJ <%IiDQdW ܝ˰H8;~ݪѲD"!n=v2%g Jbt:@ #C@DTiowp@]"w~6ݷ0 Í&ܛ~ jju% x •~ӽe^>UECtQE> E>Bլ̇Z4Y2=/X3\C{yF)sVzgR2Nw*Jp@ASɈ{IvC< Br%nb;D o dEE0vRr0@fl}ߋ`]Q# pXaSHaH2Vٔq*'@H4!@ hs195߸C lV<0=?*(;sdzng.r݆@#Q ]@S$4efNXqB,,| h,J*EB0obտ )}pz%u̪!w6@.%Сb_݉"`J@?,a_%=; +Yi7n~bOQgR>d-M#K}I9 jσ:|;hvX܅CTYÖp_e1epi& ~Vt#]K_B|%j Ð#`lIeLSwL&@] `_ l"āG4hJ S%b@|I?M"]Jz"E_ 7yP@JLO0$! <ߋUKf* _ e Y#@Fl `9`pSpL6M:m9D ^\4c\P6A|:b)t@^.bRqe+m587 DqT/k #OE%ݼTSO>T"5A ,R s[ hۅu:=laf;m[գM0^pO\maXUq1+pe<@!jkTVƑgml!{6 ԡ1> MY`v,k(/z;Ów-R(Ȭ Zbږ'/hM;#9@(?`>n0(#xR#i'$Ǒ/\ SRk1c l4г!~V5[9۾GX>*A8Ke ~EÃ>(cWӺڟ`$py< *FL<];oo~ !(Ae0>d9뾩Q@7vJݐu4l퍁 ޖճN '$cEh1]׽[D[k_\OU燯{οk{`mC@A\riSrCRiI&k)(uHۏB/ Pu%Nxr>!G[v;К(E|+v ;~vkţ FM5գ&GB@{;Ϣ 8nA/S?s~Kx0bV+0Z,Ϻq4VA֐=*PR_d0`@tW-@ARu8Ovqh} R502n&3Eb\JmMk`r L^ pwZ 3SHT1}v.($[H]2oqZH>fhTmPJto9/+@ѿvgѝi8PH+<#7 0 0 0 0 0 0?f܋,GIENDB`freemius/assets/img/plugin-icon.gif000064400000176654147600046700013403 0ustar00GIF89a͒njjqv侾SOOa]]dddGAA:44bbbcccɹ{kʚms>k{>q=XCYYyՌ]sܡn9Yz9ҦW.g|@[˃̱_ozή:Uǘͦu˛ڿ|7Xܴk\´Ҭմ{Ѻ̼Paʻ{۲dE[IYgԮy"mlˈ}ƚɗݹn>Y{]ۭixº+:/ax.W?X.0VJ:XNn-SOȑ*(!"/U-rͧvkNyd+١.ϸٲ6W\4TYhP˫An_(S=LȓO«M'RH'70e!QUTōBi%=yQMZ%Qv{_YDe'.Tn'6-9]7[ijT> pT6b[SH,Un'?0]yZ> o_LQLX%`IEy'w>Y'ȴ+Pr JR(HE=- EfS(!c|pMS̩#jZ5BHA թRV +=jVVAu*"$AҶͫZB,`"v=^IX iÓKp 6-Zd'ۦoGQPW>(~gGK:^jf)B1Q- "ʍs&WG+ہw5-[#%v(iOZ%z%b)x`ҝ.oR/7W(i0XU .uGiW̝Б G|׾Rod)D8d]|Z#8 $`@xlI9\b8pu 8\| UIIb81e\`)ҞH~[;4#{dZa 3[$E|Nb(=sa n~T|9df󝽌"۶ SyxrL0gW7,N!M30úhQägMZ˚gb"FMj!ԨF9e]R&@)\s`JؔD φv=n+Rbi+Km`h'֍c\(ps.w^ŨI7;r+}ۋN@,^ + &ry1(042W7S@WH#䒻p7cņ$ԥ>f胆 Td'Z1'9tSy-Ÿ#I]S oh 0O۝;dNCqmw8[7(=vTӷx!9kȃ^Q V M0<--m-{vC{Y=covu|zT ŋ^>KaL*ɕ~PW-xP_kF)P<+Wp'|xp8X/+'e ؀H*0!~ǁ "h+x7(p緂1W[-2:nY5z79)iA[rWYwH*W*N`ut@wBVX~Y8*2lOHUGm*A2l]8sUhFXwy~Ty}e=hH]HsHlDh8x}džt[WRnh/yozE&aFW؉]LJFQui<ٓ>WA)CIf눒HI x} XW9GYZ(^Ibc:|.jl|g rItwgqٗ~%yuyI`ɕ(7YIsɘIiv)wwhY.9ǙIYٔ)}ilyx*ɛi[ę)*隻雕vyũ[Hω) Y{ɝivꨞ)y[ٟytg:cpjHU j9(m٠{ci[D bk D:8 tYJ*zQ+ # 6qU)y0fP P6 ?*Fz뗤JLڤNJ,R:TZVzXZ\ڥ^`b:dZfzhjlڦnpr:tZvzxz|ڧ~:Zzڨ:Zzک:Zz!! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxcprt8wtptLrXYZ`gXYZtbXYZrTRCchad,bTRCgTRCdescGeneric RGB ProfileGeneric RGB Profilemluc skSK(daDK.caES$viVN$ptBR&"ukUA*HfrFU(rhuHU(zhTWnbNO&csCZ"heIL itIT(>roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,)H*\8 6pŋ3jȱcF@C @`(S\ɒȗ0CIM1syϟ*w @*IӐ ,jA˧XB40TÊ٭(@`۫ ʝKݻx+ KU*È+N|BǏ K92˘3k6\eɊ%;#ӨS.źװc˞vmۨKw< .\Td ,ЭģK;L SνoD.zDmgozönI߀:GVa X 6v`YS)x_`VD8a w!Vhg*"bw>e߆0(еԋ+84 =.%alPPhdX6N!Zy 0!dihlfmSJ&\|矀*蠄j('QP7h PZ饖^馜v駠* 0'mq qc-K*무jhe\N .ZkK$Фl4lk^j결=-;mdk,mzH&)ֻ@U%j,l2^ĬA09 XP D\Q`(|ڪ02LV{|2u5j0Mlr1ykR]Jujq-fbϳ /lp׏Ywv}7]F2Q> [ZD٫ZU}rWnyۑfdlZ%@ٛ9smS֥3Z#/l׹ک'ۻk=%wnh30@s:`M|3ۮ/dUfot:sվn ٘  5`"H Z`@¦uBløo%vNa,/lL 68!AnS‹ &c[j3CvFaR8D%@UrA@!61GWw+)dud]m.g^8D1ix-lKpp0kؔ 9 ȟD1XP;9/z#?.,$NHurvnـ@:/)%6T ,cRQ0K6 :3мa3)a3,,m2!Z3yS 2HR)4-cN|KQzAI"Os'=_sO]`B!'<JQra7A#jvA5jғ.#?<1R!H5NcntK.SfHRiT.JLpTJժ2auOYMPu`RT1ndz4JHi261I:W+YOR5STe†*WNCXr+ ` YUKrT5h+pK|jW#;ЎֲΖ ,!%3>!ZavK)'! Sv _-}֤s`¦K"̃YJ*4zD,\mS*m72P>V_MM/d 8@G>W.mQzo.y6s^*xz!_ O4Iseb+ bw on>>qmC1s\S7x2и*[ezHƈ,J._y\(G9:pێ5'5Qa2t(כZjŪ=Y~%y%Y2$gGs8sq>!FТt{ ܙt!=jo> ;UQ> jָ;\)&.Ґ}=j[{ w"F@Kɽ|&HnD&T d4clI׽Aqв5 <a΅w9Z;w )re x7wK> ;_\8'6_.ʏ'RX;oӚ88#<_}q>vQ,WH{nu>Chnp{IT;dÞWvȺO3Gؽ&y O}¼D>~b) 7Ow%M5)J3Uom#dy:&?`: Ҥw?M["@g=/n<Ѐsrɓ_B}i+KgѰVy7K}qGycvw}Jkq+vVb~dwv׀s @Yu)Mxׁܵ{i7jh7~yr5~7awE77+Xl*P1(',h28Xz3Ȁq 4 0$&cQRiTHVxY8a 8ׁDr${k؂.rH|Z[h~'Dw1̆{6mhtHȈ+Ȃ H#u7!Ri&Vw~ vPn8\!R%v(hxH^k'1!tXxx;~Fwz8atH6R&޸dz䈇oċ xH^7[XubX V{6y.YmCI2yxIIa$II43HDn _EIs̈nٕnpmFToHIz3ٗitIF7h5I99b&Jh阀 sYɚp隱EI} (Yٜ-șّ/:Yi_9)i֛3hVxم&ٟǎ9؆*IyJyti* ʞX:tJ$ڝI':a*ʠIV:/:i2ZroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,''H*\Ç#JHŇL(XǏ C9 ʬ(Q20cʜ91͛)[@ϟeJ%УHN(ʴiN쨴UNj=Yǫ`*Y, ˶ gJpcۻ˷߿ 7ARzLPʰǐ#:5˘3ǩ̹%OV1ӨS^ͺ׺&vm۷qv}@Ӱ>8Ƒ+O9 VuSyw&O?OG~zE=-י\fEaJLzYbaqHfvhև"H,hV"h#s/j- 6Վ= 锌3$-* A#J"`eQ0y&hJ8]Eޛpi.cǠrՉӂ*wCj衈&袌6裎074[o d馜v駠*ꨤ~ZGMY\,j뭸뮼k hzEe ˲6F+VkF-_LYjזk覻,V˭⾺.l믳&HM  wJ -r\.*7,rGd{\cr. s4L7*rg[#krU.axլ h P iBFdRoY@7,  A8Oh8y_ A sD5- aSXX̢RD1bp~=M^9J6Š)px) *x'X $ QQJ8R3`&M fÑr|HYiZM>I a.?_%JJzpQ.'PgUMt<焻0,a*55d*jfOl#M ʏ|erVE!0d ONqr,4m&6Od pb\UŤn9mb;*$NhG(4M)|(̐1h 8[NDNiAURԏ@ITTUpXTzZXѮNhZQ9W:"q 뀴u" k^ꜾvN~*C RfPd'KBy 0!tT]kC2ς6YlgK2dJD\Z( :1Ii nM-ݐupT'uKf)&1E*t麗xɋ%`ENQt ~i6%mR\ a7a&qRXlk[^ G$6/ZYG"NO<ê)cy20Tm|cJal({yh,/y?\᫊yNc뇙3Xk9g̀NsA06NqԺ!ε,B(dm|e[d ]VJ1&x,h3@uq~â%ZϾ6SS{\GNrw%GU^k|1`=S<j ? %x  :҇~/z\ os1AcЮh7{辙ꀦͷNqS&[(s{[κ4u\ SM?z 󜣛Vcn[] 9 %wտ>}_*-%On?:UjO pe+wwwb%6+tX|6y77u g- !foc ȁe[6`o&){؂'M0n2G`79Ȁ:g1GR|GE|G 2$HUzWY L^X-@7(cHxffZ>&= r88X 0,V=2Q 0Ds"Vu1ljh'xȈ^&vxxhg%}W=VShXbsl8dhWVXz!qXHGEɘXai(dx[̈x08q^ՍxshhƁq`'6]hVaWH 88i^O‡ 8d !YWŐȉ7 )Jh8*7 Qx6`qCxR4sؔNyE<ٓIɕ@y9aciJ7aEZIe]q(cxy.ypk8ZԖQHO `痊{aZ|ifS陨4)n1Ft II d iw䶛Y?yy9|I˙YiEYy) ٜ5syIٚ+Xb) ٚXxٞgsQ*) z K92 : *Jx*-ё#J.y^Vz/z(QPAuBkȣ( +s8:"YjI+6ڤ-:ZnaxP[z\ڀhkE.KWi:P:le)tXJxR }z:詤 ucڧhJ()w*{{G uPj T8V?J tJ jPzzUZgZVz꨹*n@WZNJ|I ڨzIyvgZzJʠ]_zꮨjZ6oˊ js*tji:|jszPҪd*aOJx)|  "+%k7*{,۲Eʤ;-X6y ʨXʴKTۓ9pW կ 3[s'k4f๴A˵zE욵a |F[bi gڴʩ&YX Rf!ʭ\N˵[+Z@ Eʯڭ?jJ_$Tx鹣@ o*$ 6_YZ/6 K_k   NлS fn۸ċ њԻnΫ˶Z+u {6zʷM{tB뙙ɌHQ뙧yjaX",HP&\F! ..! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxcprt8wtptLrXYZ`gXYZtbXYZrTRCchad,bTRCgTRCdescGeneric RGB ProfileGeneric RGB Profilemluc skSK(daDK.caES$viVN$ptBR&"ukUA*HfrFU(rhuHU(zhTWnbNO&csCZ"heIL itIT(>roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,' H*D@Ç#JHŋظǏ CYʸ(O1@$cʜI3aʛ8Q2e͟@BIʝ ʴLP<ѩիjzSRKJ!r]VeRe Tݻ]e@CmÈ+^̸Hem`h̹gͯJMS^ͺС?WTrӽwo Iē#_<+ڶ2mp؛kΝz޷o.Х@ ~]_E'KLq(WW_O:h [-HRf8]UȄa$(!^^ʅ&bUH#h㍻x!4h=i ɖ)DT#PZRdie\nݔ\xeX)%'5餙^&[ғli"Z5u$P^pj衈&2*餔VF #駠*ꨤjꩨz:B aZ)1!w*뮼+,ʚSb KжVkfv뭵!aQHVJ +k[/wJfй8K 7찻"/TLq| ,2w[LPx,2/x'|q-4, t3v2˙lTWmXg\wuF`:[BNGjl @ TsU7~&Wτ7x)3J;nyTڂ_^b.oyQmgEzuwtw gY~Ҋ/ݑc'Ϟ ΎWo@}7`G 7x@܁//?hk5;R9]:+tc曝;|rBPrmu\7؀@ XQ _L *$鍂 ciѨ0B07) (X0 p[u>hܹ:ExD 9g"vT$|oH/fH\ LMq\ABLd!C8?~H0(ؤ(x5doL*W9'HITjZv6̥.E^8d?Ȗ 7͒P2]T.|1{䣍&%Mu3Hr;ޑrn01l4t? @ҳtvɤLp0 iA #/2]1G8фE'`&[|>DQR{hL`:bG}#RHhN'O(Mҩ,S U$1(Qݔ)M.ֱf7GU.L$E+@'P,mm[1*d !^c nկ|ڲъ!(^X#xl^ zf;Yˁ"lQW[TҖ6m-ڼfյ}m\qZ5au(QBV7buՎBnsozWkC[:w]׽U{OMʧNļ=Pmsq#XN+Uk1XDkxa׷6{6p\%d.y4*8_e RӚ^x=N^czi\C\QX*Z,shQa&X6Ǐm  jcxY1#j">3B@:G6r>πڊ;'|9r>h$n%[%yFB  t4y[&]ZӚA`N{ӰuI8!ЈVu[jXzqΩuQk_Z-ޅ.59g?{2eͥ6:f6}vƣE@9~9&iXηMWv&䬹DV;{e|~8ϛ0gBI8_e)x(9p},`t- ёlmgZ(߷:=4a-,/:ô8|ݖr>@g7"v[n6ä́f'NO>v!p ye|vf놀}GOқOWѻKWd*(]=76x;uF Ps ,A(]Xlp'~IH~d dP9b8dXfX@F')|WnXwH[jdE02SH(DkAXxY(uu yx !vV8x :21(HX /{u8v؊8tVEf8XtHew0#@p86d2Ȍ8ht26$Ifu>ȋxWP~t5PUSg؎9yYT药'gp!E ii~ i{t Tyu Id# :(M ْvHP-15IH -Q7@9DY IGh;M TYX4\ q8(kIfَ0929lɕE abV(X{ɗ),%a Zi 蘐Y9"Y#┒ǘ(9 u L`ٛ,ɚUi)X[ZYZ9GE9ezJ6޺ڬ:(zuӚڬjw*ފjJԩm=[Z{ɭFET;6:{p۱: G}xz[z9ڲF[$[+͇FX ;&j0>;}$7 bSU˴'Gg<;d Z^+l{n6BkwKYt::Iku+f۸){IEHH)媠{K: |Ftnk)'J»EU^ٺ «YH;ʩ= P` EV+ە.3ʦk5pʫkE+~齱[`Pu[˫ڿ <QɫljV騕\~ꊱ*m2HP?‡ g>@ ! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxcprt8wtptLrXYZ`gXYZtbXYZrTRCchad,bTRCgTRCdescGeneric RGB ProfileGeneric RGB Profilemluc skSK(daDK.caES$viVN$ptBR&"ukUA*HfrFU(rhuHU(zhTWnbNO&csCZ"heIL itIT(>roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,' H*\@@Ç#JHŋ Ə CI@(m2ˍ%cʜISaʛ(9 J-8*͹eFJ՝' 8W9ϗxʖ(ֱpҶ۷x˷/v+MѮQ I(^̸ǐ#KB܄P"6ACMFWS^ͺװcN]@61S >xqǓ+_ynȣKNسk ,|ӧ'VG-W^󎢷50}h'J.qEF(S( J!v$HzhՁkT"0ƨb͸h#@\4;HFipiW&\=Q9`)c楙lf'Xd%oZޚrΩgyuVUӥp j衈&袌6ꨣԟxFwGnt4w@G-xM {)$w?}Qz(@ .{K:MmQz}}Ojr7}U#AG^Rw 4@ptUUi[gz|gl$A } tci/gVn€GcHIs Zz~"HrXrV8Z7r1(4xr-=(3w:P?`GGJahpL V2!'pUfƶjVC ,RtN#eXaOQjvZ 1+X7l4VXW Ya_ƆPև:0;J,jT<{؃wⰇyOxth}ш(hƶzF0X船8||A@7HUhpHKxrvhvoxƨiXlMvHȈx&j[( vj!xG&HѸ\̷Xlx}iǑܨ%iq y12 8.Z(0a3|7fByI`]ȒMEy[XkfX9))OiWwZɎ!IiPy)>MtiIі[s6-R{nv~18)y xטYy yiƘ)ɋ&y隠i9@cioKvَ })nř-YIhI1ȃX_Q8iؙ)WiryYgGi晟)9y؞ IIyz* H{ *xw#ʡ9X&:gI3Y/ʇ4ڏُ,9*uУIBZ{KzrvH. zo٤V:HPiR:آWp@8nH*b c[i]ۨk1hPrz"Z~݇ySڧ(yy裡ʨs(|'`j١ *{کZz'驔go 1ZڤMygݘ|IXgʬZ|靭Y:JjE|Bڭj2J:ȝJҙcZJgwtj٪h ɯ橧r)ZhKʰj  + i'iY.;kI7۲($9NVgeK`ʼn6ۖ[H=2[FKZC79  u +zjH a4ʵiq^ S Wv+x'(볌 J5 b2ٴ+|;Kc{k! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxcprt8wtptLrXYZ`gXYZtbXYZrTRCchad,bTRCgTRCdescGeneric RGB ProfileGeneric RGB Profilemluc skSK(daDK.caES$viVN$ptBR&"ukUA*HfrFU(rhuHU(zhTWnbNO&csCZ"heIL itIT(>roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,' a44[4 ďȪϴӪ[ڐDDֿ[[$UN@(\ȰC^HHŋ3jܘɋ&7!CX˖0c|9fM8oS' IQG*];B eJիEmSK֨VdZa;h+lٷpɞE+-qU:wޯRx <<# +^lN3aR)kj9͠wF%qzG3j˪ubgwwUx4ēîi\3KYسk.Ҧock˟O?8 EN!R[z3Á& 6F(a,Qt3V] W($h(,N@ 4fsduDٵ6Y#B]]fԅ1)8 (>{ ])Ҁ gے\ 7/c1++=d >id\@:΂6*@P> P3Ȅ*t',O-jfѡ/+:4?LgJSA02^tq'cT]AP'DԢը6=UDAǞFKJ(r&pSsB'zEVR1hu;Iն^A7-7Dua[V' "@ػ0zkpQ %kYzןf.1HEA9t)dG[B( ejْbI`; nyZ~ pcԶ(dsnF#`-ns>7u-At (.z{bmQFW_Ⱥw.w'k_YZ)q_*W.}3eus5P5+lO'fpz; au!&8` r[bB)PY!Ц&;Paf a"vNִ&8Ś9HR+^r&f΍d"JygƎn249:4 Yd,|IǺ# %G"8*&4:!r•nh=[-GҤT%;}GC Ϧjo;YIG?٪oO||o#UEgዟz|@".cc`t{] dnȯ(TqVWoGhe~yE6?CxLqlV[WXVbH0HA+86pׁǗ{ p XMgVag2}28yp71Ѐ:Nb/gv5@r5HJp=F&@`qZ؆YF~_Hbwt[n؇H!(`MHvi=hEth~ȁmv r(=PDY400@Xb^px({xZbO07QAHrW9W 8X7(Dڔ%0Ƅȋh`(9XlxƸݵ/@S@*^qօ@XK耱-ƍe(БTpv8m3؎؊MHu"ibhyÈ ttYv[p9Xq8rHot&y.;)x%8דHɓJo?IɇXsH $(H[Q)Ixg0g ُ'Ub90)-Ygtɔ^#{nYwymƕy7`X@镈 , tiGmA9KəiY}yysOsiŖv))Y[ɚ4rɘaxiҙh|śƜΉ& by{]f(oyө=*&~9TDcxvf(@!7؜ٟٞ9i)D١yg{)Y  ڠtgH#. Xi#YJY@(ZܩyHjWqpT@XroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l! ,H*\8JHŋ3jh0 rIɓ'\I ͛8'F`ɳϕ\"ѣgӠD 0$ҫX*uׯ,4``ِ]lWoF}#Zx ˷_.iȼM+ے "- pah̹3hMӨS^͚4 Aj2 :@AyУKN5׀.yQơP>˟ׯ^ ~ a&߂ *`Fa08!!Y]@@e0#.8\- fPH墇@XCYdWWDdXhGVi%)d\kIIX~)fZ.!Yy&cf~en h^&x馞{Չԝyi衈tFgmBr%J_Rj*ꨇxJjE*몚:kna|bI,i&jafv .vu)+k`_O鈮) l' 7 I kw ,$l'Q0,4/2}!s`ui5m&GB-l -S+`B- lM`l+olp-tmwB-׹g7~ | ,-tg.$y:?nv^yWkBTn+鶋JM^^KH7wLJ#gw=)P 1 aw\=0U\N50ŏck x@x"@pUSP*H87s HHy@DR>%z +A .b~IS >0A꾂C >BP0>D -l\K HGDOXna" OB7 `} IB > CsBby͸2"sNz (ClL*WVR!x.wI,k%&qQdr\{WTyPusLew Imr0r{߬捶R@;NAC 2sHC3 1>,='v*./O?I>О:B?Od(j2hQ}Jƒ `tAhQLxZKO݀X4RV)Li}G?dGi)D JTXy9՞ huKVrTkdO o\)3]yիY+$+k: QF!+ZTIdBY^1]n"V_q"BMb=ʯmleՖ5i{#qpwKnn16rqYnf '{R.|Mo J#~! /u<ͯ~ɽ&ɭ|<(S\KXHLaմ ވtLDHx1.D\ bMbgKЇ"k*q~HnOvrp7EYq{ e+ .dipgNz 5kN3l8Ejɗ`̀^.Q%53,fD8-3 $ZыȟJ>OL+Z#tMP7L^6jUzҔ(PjYZӐjZT/ʥ}gT[l]X3ΆjC 3x.ӽnv;AmZo[>F~N }o|(E zLE>r™{>@XU@=׶&7='&bA5Gs.A?:>I{`N}c8ϣ.u`5)pZ,"dwlWxX'O[~#@7xC2坩=oSr[؏= u]OЛsh׺WM!ϾPDF~ݷ@c_)gzxsdE H3z՗}(*xA.2\aECb2ׂ.182uEFS+؄Nu>.1D(AY4Bq'byQ(0aWoFF\(3^*w~cX=h`?82rk5`ox2,)g/gzh~HtȆWXw b%r}P؉gЈ?m[V$$Sb|色Ȃ!?xV(qVsdn(`8hj qHpxz}% gjv('kǘy(q8xbf{b<荲ᨇHhxG|R&07׍H !hHqWwnai ш x&ƦY0}ّ7zx")Ə7~ "&PHH?X9=(mqcB/9lDXOI;)ܗXҒTm\9!M99QiS)fEY5q sIw v9\ŋ{ |[6l9!a٘O Z\P=Gq!陎'ayc ؚ cp>jqPlI!)"niϹ}^hiYɆpiɞ6&Yiٟ(ᙠAנZJtzJx:\:{*$0*{ yp/*\l|-9B w}yCZMښ٤N엙@wIX \_ 9ZjHyj!ڦlj4 sZbzvzy~hr*8٘SZax(Jt|ڧ丢ڨf I9h:EZ*jɨ* ;yHƪ $:* jjP:IjV1:׉Jzк*fiܚnjJت抮juxگ9)*K[ҙD:kXUcJ(v لfBځu*Sj8詊+ʲ)}Ag8V;{J  ̇|J`lEL{BFFI}1ض 晩˺~~[)fysy<;SJS9a6`F\pV+M{ˉ}+yˢkz˳Rf+lKֻ+$9t+7Z.۳ +J&*h`+}ʤKŋ;y`iz]~K۾ۍfp+Pfvb ʿO[hlj 6a<\|Ll$L"(!-j2<4\6|8:4!1pfQlHJlHN,f>\F1 ?(QłvoPfL#f8V7QAw@jPpylz\~emQ Lj\u<u`g<ɔ\eLR|\ȞL|Y,ɦ|ʨ~i/,ʟA@!ʺˬƯLIJƜʽ-|,\Ĝ<͒̿<L aˡxl|L5ּBL}=JmL]NԞroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l! ,H*\@ #JHŋ3&ЩN (%أɓ(SؑǗ/E8``8sYХK@v$H*ePNIsի9j*4dѣXÊȵYDٷp3˗~&c'N @!13ky&A3tMM:sN0!pC۸ͻo<NY<yΡKNuk&(p9ோ?|S+޶>?>zWw_}8|cF&H*ErXjTh"n EHW,"0☣֊(x%t#<. f푡N2ie~DÈMvyAYVTz f&&WdTh"VVqv9Vui矼VxjEp4$F(PJifV $*ꨠzJꩡZꨛ(P>ulfev  ٦:(F+Vkf&pKB%=pr.޺*Pko%u(ns ; (;p% $l0\r*L2. * XIaMB$zIz&@/BE %LWmu(A=3W+,}4Pb%X=t6`]jfYT|twvur4W-Gs|H(з&ww,^骟eX^B 4.\ @<\t/dlnA {#]P|ʇ}mCmr^}!܋vEu0}9/'P׾O{ T`g|B}_Ʒ|"A&c1xAߔ3b:C'">@ E)w@k5L@TbD́B (db`6pXF()SG47>(?Z㚱B:>H$HG:,X6Nztt\@ɡw2aXȻ2a?VnC @> eRhʓliH-IIA#¬ݰRLL6)X>kb/ڌUM/9FfbmJN/I<JЂ$~F|C?ʷ2'DD'JfmD $Q~~z[1qԤ h͔ =)J/iLe*H*P:ԝƨ*!;5JU~&UNӫ.j%(uAhSU|_jV:L$$)X:ֺ^U!HӾ7 `KMasNpZ$=s-_+\6##4;Wi"5,dM\VTjqlRK=RoYօƍ(Ze%_kz^յDf7ڵ*w]~%R<1 O\ķ睫p,@kI}k_}h4 [X77{ D P` ./E 7+[g$P$ 8b;W3ޛJ"{Bٱo'aɮ1qd(H7T5k92W9"M_'5?%񔧬cNv 3xT3S;:\j0zvq/!^x7Ol5/2E7o]kq#(Y4|_*ڃvF4ԯGVahg%Ht@n^w8iDϾ}f?v:{| j׬f%S#{ݪ}Iտ~oa!'zWi7km] {xSpz {&>$.hb*8mAGwmׁ8@zR!EH-Gh|JL8OB5(ʳ-4z)ׄ.au5fMB7Vlslw!sqat;qH~l@p&%`oG9ƅ؊`'jR'yQ` XwayB07،nH6H%|(Ӹc(U.wⵍj hAg.8((`({(zuQ(n始j8؏ 8|$JS̘ ɋ ِXE%hG2 xθ,8p Mx`Ȓh1)85ٍgoCr<ɑ?)՘8vt0>8 HRz0pc9|+ב^"DjIkɖ)c xIxy7樗Ediy^vrMxᘏyrVQ}əw`@ɏsٚ鏇b9quYƛ—=fɏKzlYwNZnyJzlG<֕يɟ,ik ɟIo?h9j Hh扡 j8x چ r0P١* ::0.ZwΩ&4* #z%ڣA X:oroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l! ,H*\Ç#JHŋi؉Ə CI (Sz+8˗0c2TI&c2sIϟ.ILHnDiϞ&J `ҫX9 cԯ_ dKvh& pطV\V]L{ @ap1j8PȘǐ#K˘3k̹gkBo)ɨDH\QחbÖ=m7sͻ>іu൓+_μУC7~حgߞoS#pWm赧_}NM(x ?{_h`m ; x`Y<9B7!Ya 2Xda(8bVӉ(wQN0Ψ#+bǍ/ƸxA I8em"t8 c92$xp92# ݄^$'y>[^vlnn-\IHݗzmĎw3 ƒ@߮[B[lǎmG3o{oKss'|y>nlo'}3IS 4y HPKN6`#Bv6q Ƞ7z#Taa'x&{8 tC缐& QqK|ITհ44p=vME,asj2mSl@b[m7¨e*m,e/}`dނTss #H`^$7U(uXs y/pZVJQ0'XrhЖC(OoD&\9JbLCس0ɆIZsF_aN"0iVReBnz[V (Ē3cw Ә$I=BChB%!,-FGJq&>7 ΎD)J8ȉ32r 5HETx!@( ZXͪ%RdhTJ>2@ ԯtkֺ KfOjN.̮ZVpJ) «vNX$P@x/lRe Id0Ufw?},rjQ؃B7 pKMpC:ײ9v+MDu,ݑԝnj*^Gnw=5oF]zW"(p~]wjUwO+oTwu/ p!\g߬$^ ۗ6pXቤQ1L|b{Xd-Ahs@ۆӸ)G^2xN>sKƤsxE{RpK\YN |5Ofl\6H29ex^+o|9EfϏ\@7z6FtUFˮk GMRԤFPdKc՗mZ `9/AMb-_`{Iaۢ˵ x=$PMn 6mlCf@OPwwMLOD*m9'(Ӎ[ȳf]K򳴭M8ǟJcQ'y9ZrhuesӼJZsHrʁ'd#x|[?g_MXu`ؿ?(ڑmK tT}κ9O2w{25=[y7L ja=:q${Ou}g7 {w[Xqp <H'#@O>j/1Gv/x<PCxpOz/A@p~w}tu 4;6cW~1(t %PMf28h'Xp~귁*jF hlpZ%y-l@0rb6pױ&ᧃ=(AEr.A5~^|gr$84vA)VX}vYngP^ʡ; aPeh&M0wl؆ɗ*'^Gxjz8Pq}{h뗅+&jP(wg ׉Nvq_&MvHȃ؆X(uuTo؊Opq،燈PqlHhbx x~뇇vJ؄y؎@Ep[l YybpiψɎs6%ᘑC}(V _ Wnǒ y<.zm`mh}9;v6=yhJT"ic0D鉠7MSUiYV”Xb9^6s֖orɃHsCiٗ+g|I[(WhpKi0yօ뇙zǙقQ=_f)liqs!قlY}Yz䘜vm)BI|zHũ)ykWoj y9YkI.x8ǟ qZ:y%z~ FZ9h: `9E8q" Fi ZY% *j`,Z<[WgvBz~YguJդOz~D*HW.pY \ڥ֡t e.НOgpH4ƦBswژOY|*rZs`} VFgr* 虥`Ʒ{Y`  DU[WqbZJЪZs|XeZZYZec:I*Ĺֺ:ڣ@y|κ *^Ѣګ ږz-ڂ7 +0bːmz89Ji]{eN+hDo ubʐ{[I[+mY0ۙHjq!/یK- ښt™&;:V{ډ+е`ajc[f۰WQ`pk ` xx w~||۷kk-ipmv[!q}d QAADq yeQTKk˻KP{w +I0c2۽AG;{+c%;;[`a;Ak;+ K| {pP|±IfPi( KԱ.j&L:roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,H*T #JHŋ3jL 7Iɓ4[iі!`@(sɓb˟@Wi H*fclptի;@{ʵ@i EDhNʶm˗R]nm s\f `Bh3`8qRˉ6(@gS<:ӨSⶦװc~=g۸^`nS [j͟CسkwԵ9^Q{ӫϾ㣇0pG u/ %GU 6 6\eM}T]nBGa^/ R!+8aX4዇mbvU2(,0,0iDOzqSsN3>#ГKӅVLG}^Y B ¢$iHЦ5q8dN6vEwWeh[׺%rwS0kYjV-};%=DPx,"8u+ s< tȅ\W0]SG:z5x:53 `ߵmjc4@cX0^!sE'ժ1i^b6Wo;x/DNj$&։d3mϜ]=]B]Ҙδ7N{Ӡcnja9ѨV<;Zָ_8ESy i;_Ȟ$k 3 ݬ:ͦ=lRv%Nr+zvt }0{Ikenjw,%l:ÍwmvSHoҺv_f f}!\ !v7_|i1 \YKȹu.| H7/8ȣI; ѧNuC ıG0ψ*L$Z',0Jblx_ /r_1ޥ>{.w~./~ˋ>!Og dA+ ʓw}Ҕ7y_{~ G?{5e¼|3+~A&O{3ӽzR '. BL`HCi [ޗshOw+_ >nÏ}_x777$ ;@AgxJƇ[ gʹ%x~8w~xautܱo%0q&X'h-؂138{=x) H1@8Al Q}ȂExwk`wTX+Q[tHEWNe B#Ɔ^pE@_fzk7h hg?(6xw`GppPXGgG_ YgNJVhhWgxHp6Ƌfl8aXacуgduZ(E(hW(@ʼn䘈w븎Sؘ`FuH{؏ ɐė8tWxFXOg)ӷ`v~$nؒ(p-_ؑ,H|^29^4i(h+ِm0t`PF9xeKZٓC cXIZ(`[jٔz&_ bY\h؎X\oׅULyɖŋCtq8Ii IXyyo|ٗ#bZƊ𸚺חٛYd~ }yIv(Xe|皁PٜfIZ mjP}s.(_Aɛ})ɁGQ r)5)m |Y` ڜqvyyoiY ڠ7gyy% xeWv*pE٢71 :zzfIm0:0ڣ?JV_FzPJׁGIZV)J&OT)< aJl_wejRʌ_muT'CjQo] U*s{:lSOS9eZZrb?vh>}haԇ6a7@o0=+:bQÇ;km f ۻAv+ >V_ʛfŶ[[з 1۽ qHۋϗ{K[۽0[+6ˉk/ aK8 _+; k+;LU \ !!j7J.K충36f;5-lj&D|Q*٫pNroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!<,H*\8 ưŋ3jȱcň C:Ǔ(S\QZȗ0#J#y͛8s.ɳ̒: jѧѣ?$ʴ)KP>8ѩի JIX2=ֵّ4Ū%(e۷pչݻxݫw_+WZ%^̸ǐ#KLeŘ!C:1eϠC-ӗ9g$װc˾̡Wͻ azȓ*VʣK͜&Чgλ:Wo}+xG/o𑳗}|:VyY& ' R׹=x RXFaݝ!$ơO#d'__1Y%$8x<@9BD9dHzp$S FVLnTФY6F +t`dihYL6 Umx]٤A-  pDghv96n~Tcåf)1Ԃ/]eZ1H,驨J.h&I 2gzhĂPO["+l̪XARk9.mDυ+nYr] QAR f V໘8fgw1 yK\#4fr&2ɢEb=8ha;17)b|g;VL-/3,46 +ִgb[:vkh_{]ٹ&ʆVL] wH88lxP}iWngytQGko$ꬷ)$mH' 4)ҷFv_wGtuٽͬF́s~ӳ+U _GiOby  :ؔ5J.`[s:ucG:ޯ=`7B"}X0$J\"7'FQSorE,fш"K/:$coāj|  1qsQTr ޱQIBHSNS?R4SXab((#X' b\%ȎqD'?JVJ&9Tʐte+w K"%T! Kc)]2$%TA SS*1%VA@=̍򙩴*9d':M ṛ mڳG>yx=שnbdԧ&Oyg>((AM^dž:@F=IщQ M(?яr;$LKjQ`48UcKBt25iF4ӛ~it@pQuXGyiВLٔ+]+Fl 7wp uGٕ^ds Vlpcnɕ=o T2z}d@[u^ɘ'Wr()ٙ(n_燊6ky|IkٖImalpgj yd\jpeĉ'vFrwY`i مiyr[Im{i~Ii{0)QDiSM9)*ɞz8`9k牞iv8I^iٞUdZY`m9ř:vٙ*i*JI_琏ؔ*!!ClW֛Pd,*-*x>[oxD.wEHyA)FCywXE fɥEkzsSIydxJs3ʢIRH隁 Vtxʨ֧yFLYjmm\ N2JJ8Qv)L:{k(ȩD` ׉>vQڎP ЭڭycXJ**h9ʣ*p2BPIJ~*qqj6:jXwS촰 ۍЯNtk"Nצ tghv;(Yʆ~i,F[Ѓ+J֦ *,J~P @Zny2;(Q 0`{8'K~ǂA%I+fpjj~;[h@X`[L! u  kj+{v[e e𺩻뫥[LQQRP[j[6KTe׼Kk"`&UYD'ۼʻO 39~軾{ [kKaa ,^q˼ <A<\\#{[˽+\-, 5\° )ܸ?l0lL2ĺD,eO ő{:,E8b< ӠňU{af,hO'l,ƶ[k^bx\}\L< \_^l,Ql^`9,d  }\û\Ȣ<;freemius/assets/img/index.php000064400000000127147600046700012265 0ustar00k{>q=XCYYyՌ]sܡn9Yz9ҦW.g|@[˃̱_ozή:Uǘͦu˛ڿ|7Xܴk\´Ҭմ{Ѻ̼Paʻ{۲dE[IYgԮy"mlˈ}ƚɗݹn>Y{]ۭixº+:/ax.W?X.0VJ:XNn-SOȑ*(!"/U-rͧvkNyd+١.ϸٲ6W\4TYhP˫An_(S=LȓO«M'RH'70e!QUTōBi%=yQMZ%Qv{_YDe'.Tn'6-9]7[ijT> pT6b[SH,Un'?0]yZ> o_LQLX%`IEy'w>Y'ȴ+Pr JR(HE=- EfS(!c|pMS̩#jZ5BHA թRV +=jVVAu*"$AҶͫZB,`"v=^IX iÓKp 6-Zd'ۦoGQPW>(~gGK:^jf)B1Q- "ʍs&WG+ہw5-[#%v(iOZ%z%b)x`ҝ.oR/7W(i0XU .uGiW̝Б G|׾Rod)D8d]|Z#8 $`@xlI9\b8pu 8\| UIIb81e\`)ҞH~[;4#{dZa 3[$E|Nb(=sa n~T|9df󝽌"۶ SyxrL0gW7,N!M30úhQägMZ˚gb"FMj!ԨF9e]R&@)\s`JؔD φv=n+Rbi+Km`h'֍c\(ps.w^ŨI7;r+}ۋN@,^ + &ry1(042W7S@WH#䒻p7cņ$ԥ>f胆 Td'Z1'9tSy-Ÿ#I]S oh 0O۝;dNCqmw8[7(=vTӷx!9kȃ^Q V M0<--m-{vC{Y=covu|zT ŋ^>KaL*ɕ~PW-xP_kF)P<+Wp'|xp8X/+'e ؀H*0!~ǁ "h+x7(p緂1W[-2:nY5z79)iA[rWYwH*W*N`ut@wBVX~Y8*2lOHUGm*A2l]8sUhFXwy~Ty}e=hH]HsHlDh8x}džt[WRnh/yozE&aFW؉]LJFQui<ٓ>WA)CIf눒HI x} XW9GYZ(^Ibc:|.jl|g rItwgqٗ~%yuyI`ɕ(7YIsɘIiv)wwhY.9ǙIYٔ)}ilyx*ɛi[ę)*隻雕vyũ[Hω) Y{ɝivꨞ)y[ٟytg:cpjHU j9(m٠{ci[D bk D:8 tYJ*zQ+ # 6qU)y0fP P6 ?*Fz뗤JLڤNJ,R:TZVzXZ\ڥ^`b:dZfzhjlڦnpr:tZvzxz|ڧ~:Zzڨ:Zzک:Zz!! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxcprt8wtptLrXYZ`gXYZtbXYZrTRCchad,bTRCgTRCdescGeneric RGB ProfileGeneric RGB Profilemluc skSK(daDK.caES$viVN$ptBR&"ukUA*HfrFU(rhuHU(zhTWnbNO&csCZ"heIL itIT(>roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,)H*\8 6pŋ3jȱcF@C @`(S\ɒȗ0CIM1syϟ*w @*IӐ ,jA˧XB40TÊ٭(@`۫ ʝKݻx+ KU*È+N|BǏ K92˘3k6\eɊ%;#ӨS.źװc˞vmۨKw< .\Td ,ЭģK;L SνoD.zDmgozönI߀:GVa X 6v`YS)x_`VD8a w!Vhg*"bw>e߆0(еԋ+84 =.%alPPhdX6N!Zy 0!dihlfmSJ&\|矀*蠄j('QP7h PZ饖^馜v駠* 0'mq qc-K*무jhe\N .ZkK$Фl4lk^j결=-;mdk,mzH&)ֻ@U%j,l2^ĬA09 XP D\Q`(|ڪ02LV{|2u5j0Mlr1ykR]Jujq-fbϳ /lp׏Ywv}7]F2Q> [ZD٫ZU}rWnyۑfdlZ%@ٛ9smS֥3Z#/l׹ک'ۻk=%wnh30@s:`M|3ۮ/dUfot:sվn ٘  5`"H Z`@¦uBløo%vNa,/lL 68!AnS‹ &c[j3CvFaR8D%@UrA@!61GWw+)dud]m.g^8D1ix-lKpp0kؔ 9 ȟD1XP;9/z#?.,$NHurvnـ@:/)%6T ,cRQ0K6 :3мa3)a3,,m2!Z3yS 2HR)4-cN|KQzAI"Os'=_sO]`B!'<JQra7A#jvA5jғ.#?<1R!H5NcntK.SfHRiT.JLpTJժ2auOYMPu`RT1ndz4JHi261I:W+YOR5STe†*WNCXr+ ` YUKrT5h+pK|jW#;ЎֲΖ ,!%3>!ZavK)'! Sv _-}֤s`¦K"̃YJ*4zD,\mS*m72P>V_MM/d 8@G>W.mQzo.y6s^*xz!_ O4Iseb+ bw on>>qmC1s\S7x2и*[ezHƈ,J._y\(G9:pێ5'5Qa2t(כZjŪ=Y~%y%Y2$gGs8sq>!FТt{ ܙt!=jo> ;UQ> jָ;\)&.Ґ}=j[{ w"F@Kɽ|&HnD&T d4clI׽Aqв5 <a΅w9Z;w )re x7wK> ;_\8'6_.ʏ'RX;oӚ88#<_}q>vQ,WH{nu>Chnp{IT;dÞWvȺO3Gؽ&y O}¼D>~b) 7Ow%M5)J3Uom#dy:&?`: Ҥw?M["@g=/n<Ѐsrɓ_B}i+KgѰVy7K}qGycvw}Jkq+vVb~dwv׀s @Yu)Mxׁܵ{i7jh7~yr5~7awE77+Xl*P1(',h28Xz3Ȁq 4 0$&cQRiTHVxY8a 8ׁDr${k؂.rH|Z[h~'Dw1̆{6mhtHȈ+Ȃ H#u7!Ri&Vw~ vPn8\!R%v(hxH^k'1!tXxx;~Fwz8atH6R&޸dz䈇oċ xH^7[XubX V{6y.YmCI2yxIIa$II43HDn _EIs̈nٕnpmFToHIz3ٗitIF7h5I99b&Jh阀 sYɚp隱EI} (Yٜ-șّ/:Yi_9)i֛3hVxم&ٟǎ9؆*IyJyti* ʞX:tJ$ڝI':a*ʠIV:/:i2ZroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,''H*\Ç#JHŇL(XǏ C9 ʬ(Q20cʜ91͛)[@ϟeJ%УHN(ʴiN쨴UNj=Yǫ`*Y, ˶ gJpcۻ˷߿ 7ARzLPʰǐ#:5˘3ǩ̹%OV1ӨS^ͺ׺&vm۷qv}@Ӱ>8Ƒ+O9 VuSyw&O?OG~zE=-י\fEaJLzYbaqHfvhև"H,hV"h#s/j- 6Վ= 锌3$-* A#J"`eQ0y&hJ8]Eޛpi.cǠrՉӂ*wCj衈&袌6裎074[o d馜v駠*ꨤ~ZGMY\,j뭸뮼k hzEe ˲6F+VkF-_LYjזk覻,V˭⾺.l믳&HM  wJ -r\.*7,rGd{\cr. s4L7*rg[#krU.axլ h P iBFdRoY@7,  A8Oh8y_ A sD5- aSXX̢RD1bp~=M^9J6Š)px) *x'X $ QQJ8R3`&M fÑr|HYiZM>I a.?_%JJzpQ.'PgUMt<焻0,a*55d*jfOl#M ʏ|erVE!0d ONqr,4m&6Od pb\UŤn9mb;*$NhG(4M)|(̐1h 8[NDNiAURԏ@ITTUpXTzZXѮNhZQ9W:"q 뀴u" k^ꜾvN~*C RfPd'KBy 0!tT]kC2ς6YlgK2dJD\Z( :1Ii nM-ݐupT'uKf)&1E*t麗xɋ%`ENQt ~i6%mR\ a7a&qRXlk[^ G$6/ZYG"NO<ê)cy20Tm|cJal({yh,/y?\᫊yNc뇙3Xk9g̀NsA06NqԺ!ε,B(dm|e[d ]VJ1&x,h3@uq~â%ZϾ6SS{\GNrw%GU^k|1`=S<j ? %x  :҇~/z\ os1AcЮh7{辙ꀦͷNqS&[(s{[κ4u\ SM?z 󜣛Vcn[] 9 %wտ>}_*-%On?:UjO pe+wwwb%6+tX|6y77u g- !foc ȁe[6`o&){؂'M0n2G`79Ȁ:g1GR|GE|G 2$HUzWY L^X-@7(cHxffZ>&= r88X 0,V=2Q 0Ds"Vu1ljh'xȈ^&vxxhg%}W=VShXbsl8dhWVXz!qXHGEɘXai(dx[̈x08q^ՍxshhƁq`'6]hVaWH 88i^O‡ 8d !YWŐȉ7 )Jh8*7 Qx6`qCxR4sؔNyE<ٓIɕ@y9aciJ7aEZIe]q(cxy.ypk8ZԖQHO `痊{aZ|ifS陨4)n1Ft II d iw䶛Y?yy9|I˙YiEYy) ٜ5syIٚ+Xb) ٚXxٞgsQ*) z K92 : *Jx*-ё#J.y^Vz/z(QPAuBkȣ( +s8:"YjI+6ڤ-:ZnaxP[z\ڀhkE.KWi:P:le)tXJxR }z:詤 ucڧhJ()w*{{G uPj T8V?J tJ jPzzUZgZVz꨹*n@WZNJ|I ڨzIyvgZzJʠ]_zꮨjZ6oˊ js*tji:|jszPҪd*aOJx)|  "+%k7*{,۲Eʤ;-X6y ʨXʴKTۓ9pW կ 3[s'k4f๴A˵zE욵a |F[bi gڴʩ&YX Rf!ʭ\N˵[+Z@ Eʯڭ?jJ_$Tx鹣@ o*$ 6_YZ/6 K_k   NлS fn۸ċ њԻnΫ˶Z+u {6zʷM{tB뙙ɌHQ뙧yjaX",HP&\F! ..! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxcprt8wtptLrXYZ`gXYZtbXYZrTRCchad,bTRCgTRCdescGeneric RGB ProfileGeneric RGB Profilemluc skSK(daDK.caES$viVN$ptBR&"ukUA*HfrFU(rhuHU(zhTWnbNO&csCZ"heIL itIT(>roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,' H*D@Ç#JHŋظǏ CYʸ(O1@$cʜI3aʛ8Q2e͟@BIʝ ʴLP<ѩիjzSRKJ!r]VeRe Tݻ]e@CmÈ+^̸Hem`h̹gͯJMS^ͺС?WTrӽwo Iē#_<+ڶ2mp؛kΝz޷o.Х@ ~]_E'KLq(WW_O:h [-HRf8]UȄa$(!^^ʅ&bUH#h㍻x!4h=i ɖ)DT#PZRdie\nݔ\xeX)%'5餙^&[ғli"Z5u$P^pj衈&2*餔VF #駠*ꨤjꩨz:B aZ)1!w*뮼+,ʚSb KжVkfv뭵!aQHVJ +k[/wJfй8K 7찻"/TLq| ,2w[LPx,2/x'|q-4, t3v2˙lTWmXg\wuF`:[BNGjl @ TsU7~&Wτ7x)3J;nyTڂ_^b.oyQmgEzuwtw gY~Ҋ/ݑc'Ϟ ΎWo@}7`G 7x@܁//?hk5;R9]:+tc曝;|rBPrmu\7؀@ XQ _L *$鍂 ciѨ0B07) (X0 p[u>hܹ:ExD 9g"vT$|oH/fH\ LMq\ABLd!C8?~H0(ؤ(x5doL*W9'HITjZv6̥.E^8d?Ȗ 7͒P2]T.|1{䣍&%Mu3Hr;ޑrn01l4t? @ҳtvɤLp0 iA #/2]1G8фE'`&[|>DQR{hL`:bG}#RHhN'O(Mҩ,S U$1(Qݔ)M.ֱf7GU.L$E+@'P,mm[1*d !^c nկ|ڲъ!(^X#xl^ zf;Yˁ"lQW[TҖ6m-ڼfյ}m\qZ5au(QBV7buՎBnsozWkC[:w]׽U{OMʧNļ=Pmsq#XN+Uk1XDkxa׷6{6p\%d.y4*8_e RӚ^x=N^czi\C\QX*Z,shQa&X6Ǐm  jcxY1#j">3B@:G6r>πڊ;'|9r>h$n%[%yFB  t4y[&]ZӚA`N{ӰuI8!ЈVu[jXzqΩuQk_Z-ޅ.59g?{2eͥ6:f6}vƣE@9~9&iXηMWv&䬹DV;{e|~8ϛ0gBI8_e)x(9p},`t- ёlmgZ(߷:=4a-,/:ô8|ݖr>@g7"v[n6ä́f'NO>v!p ye|vf놀}GOқOWѻKWd*(]=76x;uF Ps ,A(]Xlp'~IH~d dP9b8dXfX@F')|WnXwH[jdE02SH(DkAXxY(uu yx !vV8x :21(HX /{u8v؊8tVEf8XtHew0#@p86d2Ȍ8ht26$Ifu>ȋxWP~t5PUSg؎9yYT药'gp!E ii~ i{t Tyu Id# :(M ْvHP-15IH -Q7@9DY IGh;M TYX4\ q8(kIfَ0929lɕE abV(X{ɗ),%a Zi 蘐Y9"Y#┒ǘ(9 u L`ٛ,ɚUi)X[ZYZ9GE9ezJ6޺ڬ:(zuӚڬjw*ފjJԩm=[Z{ɭFET;6:{p۱: G}xz[z9ڲF[$[+͇FX ;&j0>;}$7 bSU˴'Gg<;d Z^+l{n6BkwKYt::Iku+f۸){IEHH)媠{K: |Ftnk)'J»EU^ٺ «YH;ʩ= P` EV+ە.3ʦk5pʫkE+~齱[`Pu[˫ڿ <QɫljV騕\~ꊱ*m2HP?‡ g>@ ! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxcprt8wtptLrXYZ`gXYZtbXYZrTRCchad,bTRCgTRCdescGeneric RGB ProfileGeneric RGB Profilemluc skSK(daDK.caES$viVN$ptBR&"ukUA*HfrFU(rhuHU(zhTWnbNO&csCZ"heIL itIT(>roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,' H*\@@Ç#JHŋ Ə CI@(m2ˍ%cʜISaʛ(9 J-8*͹eFJ՝' 8W9ϗxʖ(ֱpҶ۷x˷/v+MѮQ I(^̸ǐ#KB܄P"6ACMFWS^ͺװcN]@61S >xqǓ+_ynȣKNسk ,|ӧ'VG-W^󎢷50}h'J.qEF(S( J!v$HzhՁkT"0ƨb͸h#@\4;HFipiW&\=Q9`)c楙lf'Xd%oZޚrΩgyuVUӥp j衈&袌6ꨣԟxFwGnt4w@G-xM {)$w?}Qz(@ .{K:MmQz}}Ojr7}U#AG^Rw 4@ptUUi[gz|gl$A } tci/gVn€GcHIs Zz~"HrXrV8Z7r1(4xr-=(3w:P?`GGJahpL V2!'pUfƶjVC ,RtN#eXaOQjvZ 1+X7l4VXW Ya_ƆPև:0;J,jT<{؃wⰇyOxth}ш(hƶzF0X船8||A@7HUhpHKxrvhvoxƨiXlMvHȈx&j[( vj!xG&HѸ\̷Xlx}iǑܨ%iq y12 8.Z(0a3|7fByI`]ȒMEy[XkfX9))OiWwZɎ!IiPy)>MtiIі[s6-R{nv~18)y xטYy yiƘ)ɋ&y隠i9@cioKvَ })nř-YIhI1ȃX_Q8iؙ)WiryYgGi晟)9y؞ IIyz* H{ *xw#ʡ9X&:gI3Y/ʇ4ڏُ,9*uУIBZ{KzrvH. zo٤V:HPiR:آWp@8nH*b c[i]ۨk1hPrz"Z~݇ySڧ(yy裡ʨs(|'`j١ *{کZz'驔go 1ZڤMygݘ|IXgʬZ|靭Y:JjE|Bڭj2J:ȝJҙcZJgwtj٪h ɯ橧r)ZhKʰj  + i'iY.;kI7۲($9NVgeK`ʼn6ۖ[H=2[FKZC79  u +zjH a4ʵiq^ S Wv+x'(볌 J5 b2ٴ+|;Kc{k! ICCRGBG1012appl mntrRGB XYZ   acspAPPLappl-appl descodscmxcprt8wtptLrXYZ`gXYZtbXYZrTRCchad,bTRCgTRCdescGeneric RGB ProfileGeneric RGB Profilemluc skSK(daDK.caES$viVN$ptBR&"ukUA*HfrFU(rhuHU(zhTWnbNO&csCZ"heIL itIT(>roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,' a44[4 ďȪϴӪ[ڐDDֿ[[$UN@(\ȰC^HHŋ3jܘɋ&7!CX˖0c|9fM8oS' IQG*];B eJիEmSK֨VdZa;h+lٷpɞE+-qU:wޯRx <<# +^lN3aR)kj9͠wF%qzG3j˪ubgwwUx4ēîi\3KYسk.Ҧock˟O?8 EN!R[z3Á& 6F(a,Qt3V] W($h(,N@ 4fsduDٵ6Y#B]]fԅ1)8 (>{ ])Ҁ gے\ 7/c1++=d >id\@:΂6*@P> P3Ȅ*t',O-jfѡ/+:4?LgJSA02^tq'cT]AP'DԢը6=UDAǞFKJ(r&pSsB'zEVR1hu;Iն^A7-7Dua[V' "@ػ0zkpQ %kYzןf.1HEA9t)dG[B( ejْbI`; nyZ~ pcԶ(dsnF#`-ns>7u-At (.z{bmQFW_Ⱥw.w'k_YZ)q_*W.}3eus5P5+lO'fpz; au!&8` r[bB)PY!Ц&;Paf a"vNִ&8Ś9HR+^r&f΍d"JygƎn249:4 Yd,|IǺ# %G"8*&4:!r•nh=[-GҤT%;}GC Ϧjo;YIG?٪oO||o#UEgዟz|@".cc`t{] dnȯ(TqVWoGhe~yE6?CxLqlV[WXVbH0HA+86pׁǗ{ p XMgVag2}28yp71Ѐ:Nb/gv5@r5HJp=F&@`qZ؆YF~_Hbwt[n؇H!(`MHvi=hEth~ȁmv r(=PDY400@Xb^px({xZbO07QAHrW9W 8X7(Dڔ%0Ƅȋh`(9XlxƸݵ/@S@*^qօ@XK耱-ƍe(БTpv8m3؎؊MHu"ibhyÈ ttYv[p9Xq8rHot&y.;)x%8דHɓJo?IɇXsH $(H[Q)Ixg0g ُ'Ub90)-Ygtɔ^#{nYwymƕy7`X@镈 , tiGmA9KəiY}yysOsiŖv))Y[ɚ4rɘaxiҙh|śƜΉ& by{]f(oyө=*&~9TDcxvf(@!7؜ٟٞ9i)D١yg{)Y  ڠtgH#. Xi#YJY@(ZܩyHjWqpT@XroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l! ,H*\8JHŋ3jh0 rIɓ'\I ͛8'F`ɳϕ\"ѣgӠD 0$ҫX*uׯ,4``ِ]lWoF}#Zx ˷_.iȼM+ے "- pah̹3hMӨS^͚4 Aj2 :@AyУKN5׀.yQơP>˟ׯ^ ~ a&߂ *`Fa08!!Y]@@e0#.8\- fPH墇@XCYdWWDdXhGVi%)d\kIIX~)fZ.!Yy&cf~en h^&x馞{Չԝyi衈tFgmBr%J_Rj*ꨇxJjE*몚:kna|bI,i&jafv .vu)+k`_O鈮) l' 7 I kw ,$l'Q0,4/2}!s`ui5m&GB-l -S+`B- lM`l+olp-tmwB-׹g7~ | ,-tg.$y:?nv^yWkBTn+鶋JM^^KH7wLJ#gw=)P 1 aw\=0U\N50ŏck x@x"@pUSP*H87s HHy@DR>%z +A .b~IS >0A꾂C >BP0>D -l\K HGDOXna" OB7 `} IB > CsBby͸2"sNz (ClL*WVR!x.wI,k%&qQdr\{WTyPusLew Imr0r{߬捶R@;NAC 2sHC3 1>,='v*./O?I>О:B?Od(j2hQ}Jƒ `tAhQLxZKO݀X4RV)Li}G?dGi)D JTXy9՞ huKVrTkdO o\)3]yիY+$+k: QF!+ZTIdBY^1]n"V_q"BMb=ʯmleՖ5i{#qpwKnn16rqYnf '{R.|Mo J#~! /u<ͯ~ɽ&ɭ|<(S\KXHLaմ ވtLDHx1.D\ bMbgKЇ"k*q~HnOvrp7EYq{ e+ .dipgNz 5kN3l8Ejɗ`̀^.Q%53,fD8-3 $ZыȟJ>OL+Z#tMP7L^6jUzҔ(PjYZӐjZT/ʥ}gT[l]X3ΆjC 3x.ӽnv;AmZo[>F~N }o|(E zLE>r™{>@XU@=׶&7='&bA5Gs.A?:>I{`N}c8ϣ.u`5)pZ,"dwlWxX'O[~#@7xC2坩=oSr[؏= u]OЛsh׺WM!ϾPDF~ݷ@c_)gzxsdE H3z՗}(*xA.2\aECb2ׂ.182uEFS+؄Nu>.1D(AY4Bq'byQ(0aWoFF\(3^*w~cX=h`?82rk5`ox2,)g/gzh~HtȆWXw b%r}P؉gЈ?m[V$$Sb|色Ȃ!?xV(qVsdn(`8hj qHpxz}% gjv('kǘy(q8xbf{b<荲ᨇHhxG|R&07׍H !hHqWwnai ш x&ƦY0}ّ7zx")Ə7~ "&PHH?X9=(mqcB/9lDXOI;)ܗXҒTm\9!M99QiS)fEY5q sIw v9\ŋ{ |[6l9!a٘O Z\P=Gq!陎'ayc ؚ cp>jqPlI!)"niϹ}^hiYɆpiɞ6&Yiٟ(ᙠAנZJtzJx:\:{*$0*{ yp/*\l|-9B w}yCZMښ٤N엙@wIX \_ 9ZjHyj!ڦlj4 sZbzvzy~hr*8٘SZax(Jt|ڧ丢ڨf I9h:EZ*jɨ* ;yHƪ $:* jjP:IjV1:׉Jzк*fiܚnjJت抮juxگ9)*K[ҙD:kXUcJ(v لfBځu*Sj8詊+ʲ)}Ag8V;{J  ̇|J`lEL{BFFI}1ض 晩˺~~[)fysy<;SJS9a6`F\pV+M{ˉ}+yˢkz˳Rf+lKֻ+$9t+7Z.۳ +J&*h`+}ʤKŋ;y`iz]~K۾ۍfp+Pfvb ʿO[hlj 6a<\|Ll$L"(!-j2<4\6|8:4!1pfQlHJlHN,f>\F1 ?(QłvoPfL#f8V7QAw@jPpylz\~emQ Lj\u<u`g<ɔ\eLR|\ȞL|Y,ɦ|ʨ~i/,ʟA@!ʺˬƯLIJƜʽ-|,\Ĝ<͒̿<L aˡxl|L5ּBL}=JmL]NԞroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l! ,H*\@ #JHŋ3&ЩN (%أɓ(SؑǗ/E8``8sYХK@v$H*ePNIsի9j*4dѣXÊȵYDٷp3˗~&c'N @!13ky&A3tMM:sN0!pC۸ͻo<NY<yΡKNuk&(p9ோ?|S+޶>?>zWw_}8|cF&H*ErXjTh"n EHW,"0☣֊(x%t#<. f푡N2ie~DÈMvyAYVTz f&&WdTh"VVqv9Vui矼VxjEp4$F(PJifV $*ꨠzJꩡZꨛ(P>ulfev  ٦:(F+Vkf&pKB%=pr.޺*Pko%u(ns ; (;p% $l0\r*L2. * XIaMB$zIz&@/BE %LWmu(A=3W+,}4Pb%X=t6`]jfYT|twvur4W-Gs|H(з&ww,^骟eX^B 4.\ @<\t/dlnA {#]P|ʇ}mCmr^}!܋vEu0}9/'P׾O{ T`g|B}_Ʒ|"A&c1xAߔ3b:C'">@ E)w@k5L@TbD́B (db`6pXF()SG47>(?Z㚱B:>H$HG:,X6Nztt\@ɡw2aXȻ2a?VnC @> eRhʓliH-IIA#¬ݰRLL6)X>kb/ڌUM/9FfbmJN/I<JЂ$~F|C?ʷ2'DD'JfmD $Q~~z[1qԤ h͔ =)J/iLe*H*P:ԝƨ*!;5JU~&UNӫ.j%(uAhSU|_jV:L$$)X:ֺ^U!HӾ7 `KMasNpZ$=s-_+\6##4;Wi"5,dM\VTjqlRK=RoYօƍ(Ze%_kz^յDf7ڵ*w]~%R<1 O\ķ睫p,@kI}k_}h4 [X77{ D P` ./E 7+[g$P$ 8b;W3ޛJ"{Bٱo'aɮ1qd(H7T5k92W9"M_'5?%񔧬cNv 3xT3S;:\j0zvq/!^x7Ol5/2E7o]kq#(Y4|_*ڃvF4ԯGVahg%Ht@n^w8iDϾ}f?v:{| j׬f%S#{ݪ}Iտ~oa!'zWi7km] {xSpz {&>$.hb*8mAGwmׁ8@zR!EH-Gh|JL8OB5(ʳ-4z)ׄ.au5fMB7Vlslw!sqat;qH~l@p&%`oG9ƅ؊`'jR'yQ` XwayB07،nH6H%|(Ӹc(U.wⵍj hAg.8((`({(zuQ(n始j8؏ 8|$JS̘ ɋ ِXE%hG2 xθ,8p Mx`Ȓh1)85ٍgoCr<ɑ?)՘8vt0>8 HRz0pc9|+ב^"DjIkɖ)c xIxy7樗Ediy^vrMxᘏyrVQ}əw`@ɏsٚ鏇b9quYƛ—=fɏKzlYwNZnyJzlG<֕يɟ,ik ɟIo?h9j Hh扡 j8x چ r0P١* ::0.ZwΩ&4* #z%ڣA X:oroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l! ,H*\Ç#JHŋi؉Ə CI (Sz+8˗0c2TI&c2sIϟ.ILHnDiϞ&J `ҫX9 cԯ_ dKvh& pطV\V]L{ @ap1j8PȘǐ#K˘3k̹gkBo)ɨDH\QחbÖ=m7sͻ>іu൓+_μУC7~حgߞoS#pWm赧_}NM(x ?{_h`m ; x`Y<9B7!Ya 2Xda(8bVӉ(wQN0Ψ#+bǍ/ƸxA I8em"t8 c92$xp92# ݄^$'y>[^vlnn-\IHݗzmĎw3 ƒ@߮[B[lǎmG3o{oKss'|y>nlo'}3IS 4y HPKN6`#Bv6q Ƞ7z#Taa'x&{8 tC缐& QqK|ITհ44p=vME,asj2mSl@b[m7¨e*m,e/}`dނTss #H`^$7U(uXs y/pZVJQ0'XrhЖC(OoD&\9JbLCس0ɆIZsF_aN"0iVReBnz[V (Ē3cw Ә$I=BChB%!,-FGJq&>7 ΎD)J8ȉ32r 5HETx!@( ZXͪ%RdhTJ>2@ ԯtkֺ KfOjN.̮ZVpJ) «vNX$P@x/lRe Id0Ufw?},rjQ؃B7 pKMpC:ײ9v+MDu,ݑԝnj*^Gnw=5oF]zW"(p~]wjUwO+oTwu/ p!\g߬$^ ۗ6pXቤQ1L|b{Xd-Ahs@ۆӸ)G^2xN>sKƤsxE{RpK\YN |5Ofl\6H29ex^+o|9EfϏ\@7z6FtUFˮk GMRԤFPdKc՗mZ `9/AMb-_`{Iaۢ˵ x=$PMn 6mlCf@OPwwMLOD*m9'(Ӎ[ȳf]K򳴭M8ǟJcQ'y9ZrhuesӼJZsHrʁ'd#x|[?g_MXu`ؿ?(ڑmK tT}κ9O2w{25=[y7L ja=:q${Ou}g7 {w[Xqp <H'#@O>j/1Gv/x<PCxpOz/A@p~w}tu 4;6cW~1(t %PMf28h'Xp~귁*jF hlpZ%y-l@0rb6pױ&ᧃ=(AEr.A5~^|gr$84vA)VX}vYngP^ʡ; aPeh&M0wl؆ɗ*'^Gxjz8Pq}{h뗅+&jP(wg ׉Nvq_&MvHȃ؆X(uuTo؊Opq،燈PqlHhbx x~뇇vJ؄y؎@Ep[l YybpiψɎs6%ᘑC}(V _ Wnǒ y<.zm`mh}9;v6=yhJT"ic0D鉠7MSUiYV”Xb9^6s֖orɃHsCiٗ+g|I[(WhpKi0yօ뇙zǙقQ=_f)liqs!قlY}Yz䘜vm)BI|zHũ)ykWoj y9YkI.x8ǟ qZ:y%z~ FZ9h: `9E8q" Fi ZY% *j`,Z<[WgvBz~YguJդOz~D*HW.pY \ڥ֡t e.НOgpH4ƦBswژOY|*rZs`} VFgr* 虥`Ʒ{Y`  DU[WqbZJЪZs|XeZZYZec:I*Ĺֺ:ڣ@y|κ *^Ѣګ ږz-ڂ7 +0bːmz89Ji]{eN+hDo ubʐ{[I[+mY0ۙHjq!/یK- ښt™&;:V{ډ+е`ajc[f۰WQ`pk ` xx w~||۷kk-ipmv[!q}d QAADq yeQTKk˻KP{w +I0c2۽AG;{+c%;;[`a;Ak;+ K| {pP|±IfPi( KԱ.j&L:roRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!,H*T #JHŋ3jL 7Iɓ4[iі!`@(sɓb˟@Wi H*fclptի;@{ʵ@i EDhNʶm˗R]nm s\f `Bh3`8qRˉ6(@gS<:ӨSⶦװc~=g۸^`nS [j͟CسkwԵ9^Q{ӫϾ㣇0pG u/ %GU 6 6\eM}T]nBGa^/ R!+8aX4዇mbvU2(,0,0iDOzqSsN3>#ГKӅVLG}^Y B ¢$iHЦ5q8dN6vEwWeh[׺%rwS0kYjV-};%=DPx,"8u+ s< tȅ\W0]SG:z5x:53 `ߵmjc4@cX0^!sE'ժ1i^b6Wo;x/DNj$&։d3mϜ]=]B]Ҙδ7N{Ӡcnja9ѨV<;Zָ_8ESy i;_Ȟ$k 3 ݬ:ͦ=lRv%Nr+zvt }0{Ikenjw,%l:ÍwmvSHoҺv_f f}!\ !v7_|i1 \YKȹu.| H7/8ȣI; ѧNuC ıG0ψ*L$Z',0Jblx_ /r_1ޥ>{.w~./~ˋ>!Og dA+ ʓw}Ҕ7y_{~ G?{5e¼|3+~A&O{3ӽzR '. BL`HCi [ޗshOw+_ >nÏ}_x777$ ;@AgxJƇ[ gʹ%x~8w~xautܱo%0q&X'h-؂138{=x) H1@8Al Q}ȂExwk`wTX+Q[tHEWNe B#Ɔ^pE@_fzk7h hg?(6xw`GppPXGgG_ YgNJVhhWgxHp6Ƌfl8aXacуgduZ(E(hW(@ʼn䘈w븎Sؘ`FuH{؏ ɐė8tWxFXOg)ӷ`v~$nؒ(p-_ؑ,H|^29^4i(h+ِm0t`PF9xeKZٓC cXIZ(`[jٔz&_ bY\h؎X\oׅULyɖŋCtq8Ii IXyyo|ٗ#bZƊ𸚺חٛYd~ }yIv(Xe|皁PٜfIZ mjP}s.(_Aɛ})ɁGQ r)5)m |Y` ڜqvyyoiY ڠ7gyy% xeWv*pE٢71 :zzfIm0:0ڣ?JV_FzPJׁGIZV)J&OT)< aJl_wejRʌ_muT'CjQo] U*s{:lSOS9eZZrb?vh>}haԇ6a7@o0=+:bQÇ;km f ۻAv+ >V_ʛfŶ[[з 1۽ qHۋϗ{K[۽0[+6ˉk/ aK8 _+; k+;LU \ !!j7J.K충36f;5-lj&D|Q*٫pNroRO$fdeDE,koKRsvSE&zhCNjaJPelGR"ptPO&nlNL(DesES&thTH$ltrTR"fiFI(hrHR(plPL,ruRU".arEG&PenUS&vVaeobecn RGB profilGenerel RGB-beskrivelsePerfil RGB genricCu hnh RGB ChungPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBltalnos RGB profilu( RGB r_icϏGenerisk RGB-profilObecn RGB profil RGB Profilo RGB genericoProfil RGB genericAllgemeines RGB-Profil| RGB \ |fn RGB cϏeNN, RGB 000000  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliGeneri ki RGB profilUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfiletextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&l!<,H*\8 ưŋ3jȱcň C:Ǔ(S\QZȗ0#J#y͛8s.ɳ̒: jѧѣ?$ʴ)KP>8ѩի JIX2=ֵّ4Ū%(e۷pչݻxݫw_+WZ%^̸ǐ#KLeŘ!C:1eϠC-ӗ9g$װc˾̡Wͻ azȓ*VʣK͜&Чgλ:Wo}+xG/o𑳗}|:VyY& ' R׹=x RXFaݝ!$ơO#d'__1Y%$8x<@9BD9dHzp$S FVLnTФY6F +t`dihYL6 Umx]٤A-  pDghv96n~Tcåf)1Ԃ/]eZ1H,驨J.h&I 2gzhĂPO["+l̪XARk9.mDυ+nYr] QAR f V໘8fgw1 yK\#4fr&2ɢEb=8ha;17)b|g;VL-/3,46 +ִgb[:vkh_{]ٹ&ʆVL] wH88lxP}iWngytQGko$ꬷ)$mH' 4)ҷFv_wGtuٽͬF́s~ӳ+U _GiOby  :ؔ5J.`[s:ucG:ޯ=`7B"}X0$J\"7'FQSorE,fш"K/:$coāj|  1qsQTr ޱQIBHSNS?R4SXab((#X' b\%ȎqD'?JVJ&9Tʐte+w K"%T! Kc)]2$%TA SS*1%VA@=̍򙩴*9d':M ṛ mڳG>yx=שnbdԧ&Oyg>((AM^dž:@F=IщQ M(?яr;$LKjQ`48UcKBt25iF4ӛ~it@pQuXGyiВLٔ+]+Fl 7wp uGٕ^ds Vlpcnɕ=o T2z}d@[u^ɘ'Wr()ٙ(n_燊6ky|IkٖImalpgj yd\jpeĉ'vFrwY`i مiyr[Im{i~Ii{0)QDiSM9)*ɞz8`9k牞iv8I^iٞUdZY`m9ř:vٙ*i*JI_琏ؔ*!!ClW֛Pd,*-*x>[oxD.wEHyA)FCywXE fɥEkzsSIydxJs3ʢIRH隁 Vtxʨ֧yFLYjmm\ N2JJ8Qv)L:{k(ȩD` ׉>vQڎP ЭڭycXJ**h9ʣ*p2BPIJ~*qqj6:jXwS촰 ۍЯNtk"Nצ tghv;(Yʆ~i,F[Ѓ+J֦ *,J~P @Zny2;(Q 0`{8'K~ǂA%I+fpjj~;[h@X`[L! u  kj+{v[e e𺩻뫥[LQQRP[j[6KTe׼Kk"`&UYD'ۼʻO 39~軾{ [kKaa ,^q˼ <A<\\#{[˽+\-, 5\° )ܸ?l0lL2ĺD,eO ő{:,E8b< ӠňU{af,hO'l,ƶ[k^bx\}\L< \_^l,Ql^`9,d  }\û\Ȣ<;freemius/assets/js/pricing/freemius-pricing.js.LICENSE.txt000064400000002151147600046700017444 0ustar00/* object-assign (c) Sindre Sorhus @license MIT */ /*! * Determine if an object is a Buffer * * @author Feross Aboukhadijeh * @license MIT */ /*! * Font Awesome Free 6.0.0 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * Copyright 2022 Fonticons, Inc. */ /** @license React v0.20.2 * scheduler.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** @license React v17.0.2 * react-dom.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** @license React v17.0.2 * react.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ freemius/assets/js/pricing/freemius-pricing.js000064400001217644147600046700015565 0ustar00/*! For license information please see freemius-pricing.js.LICENSE.txt */ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Freemius=t():e.Freemius=t()}(self,(function(){return(()=>{var e={487:e=>{var t={utf8:{stringToBytes:function(e){return t.bin.stringToBytes(unescape(encodeURIComponent(e)))},bytesToString:function(e){return decodeURIComponent(escape(t.bin.bytesToString(e)))}},bin:{stringToBytes:function(e){for(var t=[],n=0;n{var t,n;t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n={rotl:function(e,t){return e<>>32-t},rotr:function(e,t){return e<<32-t|e>>>t},endian:function(e){if(e.constructor==Number)return 16711935&n.rotl(e,8)|4278255360&n.rotl(e,24);for(var t=0;t0;e--)t.push(Math.floor(256*Math.random()));return t},bytesToWords:function(e){for(var t=[],n=0,a=0;n>>5]|=e[n]<<24-a%32;return t},wordsToBytes:function(e){for(var t=[],n=0;n<32*e.length;n+=8)t.push(e[n>>>5]>>>24-n%32&255);return t},bytesToHex:function(e){for(var t=[],n=0;n>>4).toString(16)),t.push((15&e[n]).toString(16));return t.join("")},hexToBytes:function(e){for(var t=[],n=0;n>>6*(3-i)&63)):n.push("=");return n.join("")},base64ToBytes:function(e){e=e.replace(/[^A-Z0-9+\/]/gi,"");for(var n=[],a=0,r=0;a>>6-2*r);return n}},e.exports=n},477:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var a=n(81),r=n.n(a),i=n(645),o=n.n(i)()(r());o.push([e.id,':root{--fs-ds-blue-10: #f0f6fc;--fs-ds-blue-50: #c5d9ed;--fs-ds-blue-100: #9ec2e6;--fs-ds-blue-200: #72aee6;--fs-ds-blue-300: #4f94d4;--fs-ds-blue-400: #3582c4;--fs-ds-blue-500: #2271b1;--fs-ds-blue-600: #135e96;--fs-ds-blue-700: #0a4b78;--fs-ds-blue-800: #043959;--fs-ds-blue-900: #01263a;--fs-ds-neutral-10: #f0f0f1;--fs-ds-neutral-50: #dcdcde;--fs-ds-neutral-100: #c3c4c7;--fs-ds-neutral-200: #a7aaad;--fs-ds-neutral-300: #8c8f94;--fs-ds-neutral-400: #787c82;--fs-ds-neutral-500: #646970;--fs-ds-neutral-600: #50575e;--fs-ds-neutral-700: #3c434a;--fs-ds-neutral-800: #2c3338;--fs-ds-neutral-900: #1d2327;--fs-ds-neutral-900-fade-60: rgba(29, 35, 39, .6);--fs-ds-neutral-900-fade-92: rgba(29, 35, 39, .08);--fs-ds-green-10: #b8e6bf;--fs-ds-green-100: #68de7c;--fs-ds-green-200: #1ed14b;--fs-ds-green-300: #00ba37;--fs-ds-green-400: #00a32a;--fs-ds-green-500: #008a20;--fs-ds-green-600: #007017;--fs-ds-green-700: #005c12;--fs-ds-green-800: #00450c;--fs-ds-green-900: #003008;--fs-ds-red-10: #facfd2;--fs-ds-red-100: #ffabaf;--fs-ds-red-200: #ff8085;--fs-ds-red-300: #f86368;--fs-ds-red-400: #e65054;--fs-ds-red-500: #d63638;--fs-ds-red-600: #b32d2e;--fs-ds-red-700: #8a2424;--fs-ds-red-800: #691c1c;--fs-ds-red-900: #451313;--fs-ds-yellow-10: #fcf9e8;--fs-ds-yellow-100: #f2d675;--fs-ds-yellow-200: #f0c33c;--fs-ds-yellow-300: #dba617;--fs-ds-yellow-400: #bd8600;--fs-ds-yellow-500: #996800;--fs-ds-yellow-600: #755100;--fs-ds-yellow-700: #614200;--fs-ds-yellow-800: #4a3200;--fs-ds-yellow-900: #362400;--fs-ds-white-10: #ffffff}#fs_pricing_app,#fs_pricing_wrapper{--fs-ds-theme-primary-accent-color: var(--fs-ds-blue-500);--fs-ds-theme-primary-accent-color-hover: var(--fs-ds-blue-600);--fs-ds-theme-primary-green-color: var(--fs-ds-green-500);--fs-ds-theme-primary-red-color: var(--fs-ds-red-500);--fs-ds-theme-primary-yellow-color: var(--fs-ds-yellow-500);--fs-ds-theme-error-color: var(--fs-ds-theme-primary-red-color);--fs-ds-theme-success-color: var(--fs-ds-theme-primary-green-color);--fs-ds-theme-warn-color: var(--fs-ds-theme-primary-yellow-color);--fs-ds-theme-background-color: var(--fs-ds-white-10);--fs-ds-theme-background-shade: var(--fs-ds-neutral-10);--fs-ds-theme-background-accented: var(--fs-ds-neutral-50);--fs-ds-theme-background-hover: var(--fs-ds-neutral-200);--fs-ds-theme-background-overlay: var(--fs-ds-neutral-900-fade-60);--fs-ds-theme-background-dark: var(--fs-ds-neutral-800);--fs-ds-theme-background-darkest: var(--fs-ds-neutral-900);--fs-ds-theme-text-color: var(--fs-ds-neutral-900);--fs-ds-theme-heading-text-color: var(--fs-ds-neutral-800);--fs-ds-theme-muted-text-color: var(--fs-ds-neutral-600);--fs-ds-theme-dark-background-text-color: var(--fs-ds-white-10);--fs-ds-theme-dark-background-muted-text-color: var(--fs-ds-neutral-300);--fs-ds-theme-divider-color: var(--fs-ds-theme-background-accented);--fs-ds-theme-border-color: var(--fs-ds-neutral-100);--fs-ds-theme-button-background-color: var(--fs-ds-neutral-50);--fs-ds-theme-button-background-hover-color: var(--fs-ds-neutral-200);--fs-ds-theme-button-text-color: var(--fs-ds-theme-heading-text-color);--fs-ds-theme-button-border-color: var(--fs-ds-neutral-300);--fs-ds-theme-button-border-hover-color: var(--fs-ds-neutral-600);--fs-ds-theme-button-border-focus-color: var(--fs-ds-blue-400);--fs-ds-theme-button-primary-background-color: var(--fs-ds-theme-primary-accent-color);--fs-ds-theme-button-primary-background-hover-color: var(--fs-ds-theme-primary-accent-color-hover);--fs-ds-theme-button-primary-text-color: var(--fs-ds-white-10);--fs-ds-theme-button-primary-border-color: var(--fs-ds-blue-800);--fs-ds-theme-button-primary-border-hover-color: var(--fs-ds-blue-900);--fs-ds-theme-button-primary-border-focus-color: var(--fs-ds-blue-100);--fs-ds-theme-button-disabled-border-color: var(--fs-ds-neutral-100);--fs-ds-theme-button-disabled-background-color: var(--fs-ds-neutral-50);--fs-ds-theme-button-disabled-text-color: var(--fs-ds-neutral-300);--fs-ds-theme-notice-warn-background: var(--fs-ds-yellow-10);--fs-ds-theme-notice-warn-color: var(--fs-ds-yellow-900);--fs-ds-theme-notice-warn-border: var(--fs-ds-theme-warn-color);--fs-ds-theme-notice-info-background: var(--fs-ds-theme-background-shade);--fs-ds-theme-notice-info-color: var(--fs-ds-theme-primary-accent-color-hover);--fs-ds-theme-notice-info-border: var(--fs-ds-theme-primary-accent-color);--fs-ds-theme-package-popular-background: var(--fs-ds-blue-200);--fs-ds-theme-testimonial-star-color: var(--fs-ds-yellow-300)}#fs_pricing.fs-full-size-wrapper{margin-top:0}#root,#fs_pricing_app{background:var(--fs-ds-theme-background-shade);color:var(--fs-ds-theme-text-color);height:auto;line-height:normal;font-size:13px;margin:0}#root h1,#root h2,#root h3,#root h4,#root ul,#root blockquote,#fs_pricing_app h1,#fs_pricing_app h2,#fs_pricing_app h3,#fs_pricing_app h4,#fs_pricing_app ul,#fs_pricing_app blockquote{margin:0;padding:0;text-align:center;color:var(--fs-ds-theme-heading-text-color)}#root h1,#fs_pricing_app h1{font-size:2.5em}#root h2,#fs_pricing_app h2{font-size:1.5em}#root h3,#fs_pricing_app h3{font-size:1.2em}#root ul,#fs_pricing_app ul{list-style-type:none}#root p,#fs_pricing_app p{font-size:.9em}#root p,#root blockquote,#fs_pricing_app p,#fs_pricing_app blockquote{color:var(--fs-ds-theme-text-color)}#root strong,#fs_pricing_app strong{font-weight:700}#root li,#root dd,#fs_pricing_app li,#fs_pricing_app dd{margin:0}#root .fs-app-header .fs-page-title,#fs_pricing_app .fs-app-header .fs-page-title{margin:0 0 15px;text-align:left;display:flex;flex-flow:row wrap;gap:10px;align-items:center;padding:20px 15px 10px}#root .fs-app-header .fs-page-title h1,#fs_pricing_app .fs-app-header .fs-page-title h1{font-size:18px;margin:0}#root .fs-app-header .fs-page-title h3,#fs_pricing_app .fs-app-header .fs-page-title h3{margin:0;font-size:14px;padding:4px 8px;font-weight:400;border-radius:4px;background-color:var(--fs-ds-theme-background-accented);color:var(--fs-ds-theme-muted-text-color)}#root .fs-app-header .fs-plugin-title-and-logo,#fs_pricing_app .fs-app-header .fs-plugin-title-and-logo{margin:0 15px;background:var(--fs-ds-theme-background-color);padding:12px 0;border:1px solid var(--fs-ds-theme-divider-color);border-radius:4px;text-align:center}#root .fs-app-header .fs-plugin-title-and-logo .fs-plugin-logo,#root .fs-app-header .fs-plugin-title-and-logo h1,#fs_pricing_app .fs-app-header .fs-plugin-title-and-logo .fs-plugin-logo,#fs_pricing_app .fs-app-header .fs-plugin-title-and-logo h1{display:inline-block;vertical-align:middle;margin:0 10px}#root .fs-app-header .fs-plugin-title-and-logo .fs-plugin-logo,#fs_pricing_app .fs-app-header .fs-plugin-title-and-logo .fs-plugin-logo{width:48px;height:48px;border-radius:4px}@media screen and (min-width: 601px){#root .fs-app-header .fs-plugin-title-and-logo .fs-plugin-logo,#fs_pricing_app .fs-app-header .fs-plugin-title-and-logo .fs-plugin-logo{width:64px;height:64px}}#root .fs-trial-message,#fs_pricing_app .fs-trial-message{padding:20px;background:var(--fs-ds-theme-notice-warn-background);color:var(--fs-ds-theme-notice-warn-color);font-weight:700;text-align:center;border-top:1px solid var(--fs-ds-theme-notice-warn-border);border-bottom:1px solid var(--fs-ds-theme-notice-warn-border);font-size:1.2em;box-sizing:border-box;margin:0 0 5px}#root .fs-app-main,#fs_pricing_app .fs-app-main{text-align:center}#root .fs-app-main .fs-section,#fs_pricing_app .fs-app-main .fs-section{margin:auto;display:block}#root .fs-app-main .fs-section .fs-section-header,#fs_pricing_app .fs-app-main .fs-section .fs-section-header{font-weight:700}#root .fs-app-main>.fs-section,#fs_pricing_app .fs-app-main>.fs-section{padding:20px;margin:4em auto 0}#root .fs-app-main>.fs-section:nth-child(even),#fs_pricing_app .fs-app-main>.fs-section:nth-child(even){background:var(--fs-ds-theme-background-color)}#root .fs-app-main>.fs-section>header,#fs_pricing_app .fs-app-main>.fs-section>header{margin:0 0 3em}#root .fs-app-main>.fs-section>header h2,#fs_pricing_app .fs-app-main>.fs-section>header h2{margin:0;font-size:2.5em}#root .fs-app-main .fs-section--plans-and-pricing,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing{padding:20px;margin-top:0}#root .fs-app-main .fs-section--plans-and-pricing>.fs-section,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing>.fs-section{margin:1.5em auto 0}#root .fs-app-main .fs-section--plans-and-pricing>.fs-section:first-child,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing>.fs-section:first-child{margin-top:0}#root .fs-app-main .fs-section--plans-and-pricing .fs-annual-discount,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-annual-discount{font-weight:700;font-size:small}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--trial-header,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--trial-header{text-align:center;background:var(--fs-ds-theme-background-color);padding:20px;border-radius:5px;box-sizing:border-box;max-width:945px}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--trial-header h2,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--trial-header h2{margin-bottom:10px}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--trial-header h4,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--trial-header h4{font-weight:400}#root .fs-app-main .fs-section--plans-and-pricing .fs-currencies,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-currencies{border-color:var(--fs-ds-theme-button-border-color)}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles{display:inline-block;vertical-align:middle;padding:0 10px;width:auto}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles{overflow:hidden}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles li,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles li{border:1px solid var(--fs-ds-theme-border-color);border-right-width:0;display:inline-block;font-weight:700;margin:0;padding:10px;cursor:pointer}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles li:first-child,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles li:first-child{border-radius:20px 0 0 20px}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles li:last-child,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles li:last-child{border-radius:0 20px 20px 0;border-right-width:1px}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles li.fs-selected-billing-cycle,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--billing-cycles .fs-billing-cycles li.fs-selected-billing-cycle{background:var(--fs-ds-theme-background-color);color:var(--fs-ds-theme-primary-accent-color-hover)}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--custom-implementation,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--custom-implementation{padding:15px;background:var(--fs-ds-theme-background-color);border:1px solid var(--fs-ds-theme-divider-color);border-radius:4px;box-sizing:border-box;max-width:945px;margin:0 auto}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--custom-implementation h2,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--custom-implementation h2{margin-bottom:10px;font-weight:700}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--custom-implementation p,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--custom-implementation p{font-size:small;margin:0}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--money-back-guarantee,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--money-back-guarantee{max-width:857px;margin:30px auto;position:relative}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--money-back-guarantee .fs-money-back-guarantee-title,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--money-back-guarantee .fs-money-back-guarantee-title{color:var(--fs-ds-theme-heading-text-color);font-weight:700;margin-bottom:15px}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--money-back-guarantee .fs-money-back-guarantee-message,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--money-back-guarantee .fs-money-back-guarantee-message{font-size:small;line-height:20px;margin-bottom:15px;padding:0 15px}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--money-back-guarantee img,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--money-back-guarantee img{position:absolute;width:90px;top:50%;right:0;margin-top:-45px}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--badges .fs-badge,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--badges .fs-badge{display:inline-block;vertical-align:middle;position:relative;box-shadow:none;background:transparent}#root .fs-app-main .fs-section--plans-and-pricing .fs-section--badges .fs-badge+.fs-badge,#fs_pricing_app .fs-app-main .fs-section--plans-and-pricing .fs-section--badges .fs-badge+.fs-badge{margin-left:20px;margin-top:13px}#root .fs-app-main .fs-section--testimonials,#fs_pricing_app .fs-app-main .fs-section--testimonials{border-top:1px solid var(--fs-ds-theme-border-color);border-bottom:1px solid var(--fs-ds-theme-border-color);padding:3em 4em 4em}#root .fs-app-main .fs-section--testimonials .fs-section-header,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-section-header{margin-left:-30px;margin-right:-30px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav{margin:auto;display:block;width:auto;position:relative}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-prev,#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-next,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-prev,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-next{top:50%;border:1px solid var(--fs-ds-theme-border-color);border-radius:14px;cursor:pointer;margin-top:11px;position:absolute}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-prev .fs-icon,#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-next .fs-icon,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-prev .fs-icon,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-next .fs-icon{display:inline-block;height:1em;width:1em;line-height:1em;color:var(--fs-ds-theme-muted-text-color);padding:5px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-prev,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-prev{margin-left:-30px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-next,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-nav.fs-nav-next{right:-30px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials-track,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials-track{margin:auto;overflow:hidden;position:relative;display:block;padding-top:45px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials{width:10000px;display:block;position:relative;transition:left .5s ease,right .5s ease}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial{float:left;font-size:small;position:relative;width:340px;box-sizing:border-box;margin:0}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial>section,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial>section{box-sizing:border-box}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-rating,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-rating{color:var(--fs-ds-theme-testimonial-star-color)}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header,#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial>section,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial>section{background:var(--fs-ds-theme-background-color);padding:10px;margin:0 2em;border:1px solid var(--fs-ds-theme-divider-color)}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial>section,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial>section{border-radius:0 0 8px 8px;border-top:0 none}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header{border-bottom:0 none;border-radius:8px 8px 0 0}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header .fs-testimonial-logo,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header .fs-testimonial-logo{border:1px solid var(--fs-ds-theme-divider-color);border-radius:44px;padding:5px;background:var(--fs-ds-theme-background-color);width:76px;height:76px;position:relative;margin-top:-54px;left:50%;margin-left:-44px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header .fs-testimonial-logo object,#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header .fs-testimonial-logo img,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header .fs-testimonial-logo object,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header .fs-testimonial-logo img{max-width:100%;border-radius:40px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header h4,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-header h4{margin:15px 0 6px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-icon-quote,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-icon-quote{color:var(--fs-ds-theme-muted-text-color)}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-message,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-message{line-height:18px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-author,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-author{margin-top:30px;margin-bottom:10px}#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-author .fs-testimonial-author-name,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial .fs-testimonial-author .fs-testimonial-author-name{font-weight:700;margin-bottom:2px;color:var(--fs-ds-theme-text-color)}#root .fs-app-main .fs-section--testimonials .fs-nav-pagination,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-nav-pagination{margin:4em 0 0;position:relative}#root .fs-app-main .fs-section--testimonials .fs-nav-pagination li,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-nav-pagination li{position:relative;display:inline-block;margin:0 8px}#root .fs-app-main .fs-section--testimonials .fs-nav-pagination li button.fs-round-button,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-nav-pagination li button.fs-round-button{cursor:pointer;border:1px solid var(--fs-ds-theme-border-color);vertical-align:middle;display:inline-block;line-height:0;width:8px;height:8px;padding:0;color:transparent;outline:none;border-radius:4px;overflow:hidden}#root .fs-app-main .fs-section--testimonials .fs-nav-pagination li button.fs-round-button span,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-nav-pagination li button.fs-round-button span{display:inline-block;width:100%;height:100%;background:var(--fs-ds-theme-background-shade)}#root .fs-app-main .fs-section--testimonials .fs-nav-pagination li.selected button,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-nav-pagination li.selected button{border:0 none}#root .fs-app-main .fs-section--testimonials .fs-nav-pagination li.selected button.fs-round-button span,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-nav-pagination li.selected button.fs-round-button span{background:var(--fs-ds-theme-background-accented)}#root .fs-app-main .fs-section--faq,#fs_pricing_app .fs-app-main .fs-section--faq{background:var(--fs-ds-theme-background-shade)}#root .fs-app-main .fs-section--faq .fs-section--faq-items,#fs_pricing_app .fs-app-main .fs-section--faq .fs-section--faq-items{max-width:945px;margin:0 auto;box-sizing:border-box;text-align:left;columns:2;column-gap:20px}@media only screen and (max-width: 600px){#root .fs-app-main .fs-section--faq .fs-section--faq-items,#fs_pricing_app .fs-app-main .fs-section--faq .fs-section--faq-items{columns:1}}#root .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item,#fs_pricing_app .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item{width:100%;display:inline-block;vertical-align:top;margin:0 0 20px;overflow:hidden}#root .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item h3,#root .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item p,#fs_pricing_app .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item h3,#fs_pricing_app .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item p{margin:0;text-align:left}#root .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item h3,#fs_pricing_app .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item h3{background:var(--fs-ds-theme-background-dark);color:var(--fs-ds-theme-dark-background-text-color);padding:15px;font-weight:700;border:1px solid var(--fs-ds-theme-background-darkest);border-bottom:0 none;border-radius:4px 4px 0 0}#root .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item p,#fs_pricing_app .fs-app-main .fs-section--faq .fs-section--faq-items .fs-section--faq-item p{background:var(--fs-ds-theme-background-color);font-size:small;padding:15px;line-height:20px;border:1px solid var(--fs-ds-theme-border-color);border-top:0 none;border-radius:0 0 4px 4px}#root .fs-button,#fs_pricing_app .fs-button{background:var(--fs-ds-theme-button-background-color);color:var(--fs-ds-theme-button-text-color);padding:12px 10px;display:inline-block;text-transform:uppercase;font-weight:700;font-size:18px;width:100%;border-radius:4px;border:0 none;cursor:pointer;transition:background .2s ease-out,border-bottom-color .2s ease-out}#root .fs-button:focus:not(:disabled),#fs_pricing_app .fs-button:focus:not(:disabled){box-shadow:0 0 0 1px var(--fs-ds-theme-button-border-focus-color)}#root .fs-button:hover:not(:disabled),#root .fs-button:focus:not(:disabled),#root .fs-button:active:not(:disabled),#fs_pricing_app .fs-button:hover:not(:disabled),#fs_pricing_app .fs-button:focus:not(:disabled),#fs_pricing_app .fs-button:active:not(:disabled){will-change:background,border;background:var(--fs-ds-theme-button-background-hover-color)}#root .fs-button.fs-button--outline,#fs_pricing_app .fs-button.fs-button--outline{padding-top:11px;padding-bottom:11px;background:var(--fs-ds-theme-background-color);border:1px solid var(--fs-ds-theme-button-border-color)}#root .fs-button.fs-button--outline:focus:not(:disabled),#fs_pricing_app .fs-button.fs-button--outline:focus:not(:disabled){background:var(--fs-ds-theme-background-shade);border-color:var(--fs-ds-theme-button-border-focus-color)}#root .fs-button.fs-button--outline:hover:not(:disabled),#root .fs-button.fs-button--outline:active:not(:disabled),#fs_pricing_app .fs-button.fs-button--outline:hover:not(:disabled),#fs_pricing_app .fs-button.fs-button--outline:active:not(:disabled){background:var(--fs-ds-theme-background-shade);border-color:var(--fs-ds-theme-button-border-hover-color)}#root .fs-button.fs-button--type-primary,#fs_pricing_app .fs-button.fs-button--type-primary{background-color:var(--fs-ds-theme-button-primary-background-color);color:var(--fs-ds-theme-button-primary-text-color);border-color:var(--fs-ds-theme-button-primary-border-color)}#root .fs-button.fs-button--type-primary:focus:not(:disabled),#root .fs-button.fs-button--type-primary:hover:not(:disabled),#root .fs-button.fs-button--type-primary:active:not(:disabled),#fs_pricing_app .fs-button.fs-button--type-primary:focus:not(:disabled),#fs_pricing_app .fs-button.fs-button--type-primary:hover:not(:disabled),#fs_pricing_app .fs-button.fs-button--type-primary:active:not(:disabled){background-color:var(--fs-ds-theme-button-primary-background-hover-color);border-color:var(--fs-ds-theme-button-primary-border-hover-color)}#root .fs-button.fs-button--type-primary.fs-button--outline,#fs_pricing_app .fs-button.fs-button--type-primary.fs-button--outline{background-color:var(--fs-ds-theme-background-color);color:var(--fs-ds-theme-primary-accent-color);border:1px solid var(--fs-ds-theme-button-primary-border-color)}#root .fs-button.fs-button--type-primary.fs-button--outline:focus:not(:disabled),#root .fs-button.fs-button--type-primary.fs-button--outline:hover:not(:disabled),#root .fs-button.fs-button--type-primary.fs-button--outline:active:not(:disabled),#fs_pricing_app .fs-button.fs-button--type-primary.fs-button--outline:focus:not(:disabled),#fs_pricing_app .fs-button.fs-button--type-primary.fs-button--outline:hover:not(:disabled),#fs_pricing_app .fs-button.fs-button--type-primary.fs-button--outline:active:not(:disabled){background-color:var(--fs-ds-theme-background-shade);color:var(--fs-ds-theme-button-primary-background-hover-color);border-color:var(--fs-ds-theme-primary-accent-color-hover)}#root .fs-button:disabled,#fs_pricing_app .fs-button:disabled{cursor:not-allowed;background-color:var(--fs-ds-theme-button-disabled-background-color);color:var(--fs-ds-theme-button-disabled-text-color);border-color:var(--fs-ds-theme-button-disabled-border-color)}#root .fs-button.fs-button--size-small,#fs_pricing_app .fs-button.fs-button--size-small{font-size:14px;width:auto}#root .fs-placeholder:before,#fs_pricing_app .fs-placeholder:before{content:"";display:inline-block}@media only screen and (max-width: 768px){#root .fs-app-main .fs-section--testimonials .fs-nav-pagination,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-nav-pagination{display:none!important}#root .fs-app-main .fs-section>header h2,#fs_pricing_app .fs-app-main .fs-section>header h2{font-size:1.5em}}@media only screen and (max-width: 455px){#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial{width:auto}#root .fs-app-main .fs-section--billing-cycles .fs-billing-cycles li.fs-period--annual span,#fs_pricing_app .fs-app-main .fs-section--billing-cycles .fs-billing-cycles li.fs-period--annual span{display:none}}@media only screen and (max-width: 375px){#root .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial,#fs_pricing_app .fs-app-main .fs-section--testimonials .fs-testimonials-nav .fs-testimonials .fs-testimonial{width:auto}}\n',""]);const s=o},333:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var a=n(81),r=n.n(a),i=n(645),o=n.n(i)()(r());o.push([e.id,"#fs_pricing_app .fs-modal,#fs_pricing_wrapper .fs-modal,#fs_pricing_wrapper #fs_pricing_app .fs-modal{position:fixed;inset:0;z-index:1000;zoom:1;text-align:left;display:block!important}#fs_pricing_app .fs-modal .fs-modal-content-container,#fs_pricing_wrapper .fs-modal .fs-modal-content-container,#fs_pricing_wrapper #fs_pricing_app .fs-modal .fs-modal-content-container{display:block;position:absolute;left:50%;background:var(--fs-ds-theme-background-color);box-shadow:0 0 8px 2px #0000004d}#fs_pricing_app .fs-modal .fs-modal-content-container .fs-modal-header,#fs_pricing_wrapper .fs-modal .fs-modal-content-container .fs-modal-header,#fs_pricing_wrapper #fs_pricing_app .fs-modal .fs-modal-content-container .fs-modal-header{background:var(--fs-ds-theme-primary-accent-color);padding:15px}#fs_pricing_app .fs-modal .fs-modal-content-container .fs-modal-header h3,#fs_pricing_app .fs-modal .fs-modal-content-container .fs-modal-header .fs-modal-close,#fs_pricing_wrapper .fs-modal .fs-modal-content-container .fs-modal-header h3,#fs_pricing_wrapper .fs-modal .fs-modal-content-container .fs-modal-header .fs-modal-close,#fs_pricing_wrapper #fs_pricing_app .fs-modal .fs-modal-content-container .fs-modal-header h3,#fs_pricing_wrapper #fs_pricing_app .fs-modal .fs-modal-content-container .fs-modal-header .fs-modal-close{color:var(--fs-ds-theme-background-color)}#fs_pricing_app .fs-modal .fs-modal-content-container .fs-modal-content,#fs_pricing_wrapper .fs-modal .fs-modal-content-container .fs-modal-content,#fs_pricing_wrapper #fs_pricing_app .fs-modal .fs-modal-content-container .fs-modal-content{font-size:1.2em}#fs_pricing_app .fs-modal--loading,#fs_pricing_wrapper .fs-modal--loading,#fs_pricing_wrapper #fs_pricing_app .fs-modal--loading{background-color:#0000004d}#fs_pricing_app .fs-modal--loading .fs-modal-content-container,#fs_pricing_wrapper .fs-modal--loading .fs-modal-content-container,#fs_pricing_wrapper #fs_pricing_app .fs-modal--loading .fs-modal-content-container{width:220px;margin-left:-126px;padding:15px;border:1px solid var(--fs-ds-theme-divider-color);text-align:center;top:50%}#fs_pricing_app .fs-modal--loading .fs-modal-content-container span,#fs_pricing_wrapper .fs-modal--loading .fs-modal-content-container span,#fs_pricing_wrapper #fs_pricing_app .fs-modal--loading .fs-modal-content-container span{display:block;font-weight:700;font-size:16px;text-align:center;color:var(--fs-ds-theme-primary-accent-color);margin-bottom:10px}#fs_pricing_app .fs-modal--loading .fs-modal-content-container .fs-ajax-loader,#fs_pricing_wrapper .fs-modal--loading .fs-modal-content-container .fs-ajax-loader,#fs_pricing_wrapper #fs_pricing_app .fs-modal--loading .fs-modal-content-container .fs-ajax-loader{width:160px}#fs_pricing_app .fs-modal--loading .fs-modal-content-container i,#fs_pricing_wrapper .fs-modal--loading .fs-modal-content-container i,#fs_pricing_wrapper #fs_pricing_app .fs-modal--loading .fs-modal-content-container i{display:block;width:128px;margin:0 auto;height:15px;background:url(//img.freemius.com/blue-loader.gif)}#fs_pricing_app .fs-modal--refund-policy,#fs_pricing_app .fs-modal--trial-confirmation,#fs_pricing_wrapper .fs-modal--refund-policy,#fs_pricing_wrapper .fs-modal--trial-confirmation,#fs_pricing_wrapper #fs_pricing_app .fs-modal--refund-policy,#fs_pricing_wrapper #fs_pricing_app .fs-modal--trial-confirmation{background:rgba(0,0,0,.7)}#fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container,#fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container,#fs_pricing_wrapper .fs-modal--refund-policy .fs-modal-content-container,#fs_pricing_wrapper .fs-modal--trial-confirmation .fs-modal-content-container,#fs_pricing_wrapper #fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container,#fs_pricing_wrapper #fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container{width:510px;margin-left:-255px;top:20%}#fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container .fs-modal-header .fs-modal-close,#fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-header .fs-modal-close,#fs_pricing_wrapper .fs-modal--refund-policy .fs-modal-content-container .fs-modal-header .fs-modal-close,#fs_pricing_wrapper .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-header .fs-modal-close,#fs_pricing_wrapper #fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container .fs-modal-header .fs-modal-close,#fs_pricing_wrapper #fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-header .fs-modal-close{line-height:24px;font-size:24px;position:absolute;top:-12px;right:-12px;cursor:pointer}#fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container .fs-modal-content,#fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-content,#fs_pricing_wrapper .fs-modal--refund-policy .fs-modal-content-container .fs-modal-content,#fs_pricing_wrapper .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-content,#fs_pricing_wrapper #fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container .fs-modal-content,#fs_pricing_wrapper #fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-content{height:100%;padding:1px 15px}#fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container .fs-modal-footer,#fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-footer,#fs_pricing_wrapper .fs-modal--refund-policy .fs-modal-content-container .fs-modal-footer,#fs_pricing_wrapper .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-footer,#fs_pricing_wrapper #fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container .fs-modal-footer,#fs_pricing_wrapper #fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-footer{padding:10px;text-align:right;border-top:1px solid var(--fs-ds-theme-border-color);background:var(--fs-ds-theme-background-shade)}#fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container .fs-modal-footer .fs-button--approve-trial,#fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-footer .fs-button--approve-trial,#fs_pricing_wrapper .fs-modal--refund-policy .fs-modal-content-container .fs-modal-footer .fs-button--approve-trial,#fs_pricing_wrapper .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-footer .fs-button--approve-trial,#fs_pricing_wrapper #fs_pricing_app .fs-modal--refund-policy .fs-modal-content-container .fs-modal-footer .fs-button--approve-trial,#fs_pricing_wrapper #fs_pricing_app .fs-modal--trial-confirmation .fs-modal-content-container .fs-modal-footer .fs-button--approve-trial{margin:0 7px}#fs_pricing_app .fs-modal--trial-confirmation .fs-button,#fs_pricing_wrapper .fs-modal--trial-confirmation .fs-button,#fs_pricing_wrapper #fs_pricing_app .fs-modal--trial-confirmation .fs-button{width:auto;font-size:13px}\n",""]);const s=o},267:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var a=n(81),r=n.n(a),i=n(645),o=n.n(i)()(r());o.push([e.id,'#root .fs-package,#fs_pricing_app .fs-package{display:inline-block;vertical-align:top;background:var(--fs-ds-theme-dark-background-text-color);border-bottom:3px solid var(--fs-ds-theme-border-color);width:315px;box-sizing:border-box}#root .fs-package:first-child,#root .fs-package+.fs-package,#fs_pricing_app .fs-package:first-child,#fs_pricing_app .fs-package+.fs-package{border-left:1px solid var(--fs-ds-theme-divider-color)}#root .fs-package:last-child,#fs_pricing_app .fs-package:last-child{border-right:1px solid var(--fs-ds-theme-divider-color)}#root .fs-package:not(.fs-featured-plan):first-child,#fs_pricing_app .fs-package:not(.fs-featured-plan):first-child{border-top-left-radius:10px}#root .fs-package:not(.fs-featured-plan):first-child .fs-plan-title,#fs_pricing_app .fs-package:not(.fs-featured-plan):first-child .fs-plan-title{border-top-left-radius:9px}#root .fs-package:not(.fs-featured-plan):last-child,#fs_pricing_app .fs-package:not(.fs-featured-plan):last-child{border-top-right-radius:10px}#root .fs-package:not(.fs-featured-plan):last-child .fs-plan-title,#fs_pricing_app .fs-package:not(.fs-featured-plan):last-child .fs-plan-title{border-top-right-radius:9px}#root .fs-package .fs-package-content,#fs_pricing_app .fs-package .fs-package-content{vertical-align:middle;padding-bottom:30px}#root .fs-package .fs-plan-title,#fs_pricing_app .fs-package .fs-plan-title{padding:10px 0;background:var(--fs-ds-theme-background-shade);text-transform:uppercase;border-bottom:1px solid var(--fs-ds-theme-divider-color);border-top:1px solid var(--fs-ds-theme-divider-color);width:100%;text-align:center}#root .fs-package .fs-plan-title:last-child,#fs_pricing_app .fs-package .fs-plan-title:last-child{border-right:none}#root .fs-package .fs-plan-description,#root .fs-package .fs-undiscounted-price,#root .fs-package .fs-licenses,#root .fs-package .fs-upgrade-button,#root .fs-package .fs-plan-features,#fs_pricing_app .fs-package .fs-plan-description,#fs_pricing_app .fs-package .fs-undiscounted-price,#fs_pricing_app .fs-package .fs-licenses,#fs_pricing_app .fs-package .fs-upgrade-button,#fs_pricing_app .fs-package .fs-plan-features{margin-top:10px}#root .fs-package .fs-plan-description,#fs_pricing_app .fs-package .fs-plan-description{text-transform:uppercase}#root .fs-package .fs-undiscounted-price,#fs_pricing_app .fs-package .fs-undiscounted-price{margin:auto;position:relative;display:inline-block;color:var(--fs-ds-theme-muted-text-color);top:6px}#root .fs-package .fs-undiscounted-price:after,#fs_pricing_app .fs-package .fs-undiscounted-price:after{display:block;content:"";position:absolute;height:1px;background-color:var(--fs-ds-theme-error-color);left:-4px;right:-4px;top:50%;transform:translateY(-50%) skewY(1deg)}#root .fs-package .fs-selected-pricing-amount,#fs_pricing_app .fs-package .fs-selected-pricing-amount{margin:5px 0}#root .fs-package .fs-selected-pricing-amount .fs-currency-symbol,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-currency-symbol{font-size:39px}#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer{font-size:58px;margin:0 5px}#root .fs-package .fs-selected-pricing-amount .fs-currency-symbol,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-currency-symbol,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container{display:inline-block;vertical-align:middle}#root .fs-package .fs-selected-pricing-amount .fs-currency-symbol:not(.fs-selected-pricing-amount-integer),#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer:not(.fs-selected-pricing-amount-integer),#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container:not(.fs-selected-pricing-amount-integer),#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-currency-symbol:not(.fs-selected-pricing-amount-integer),#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer:not(.fs-selected-pricing-amount-integer),#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container:not(.fs-selected-pricing-amount-integer){line-height:18px}#root .fs-package .fs-selected-pricing-amount .fs-currency-symbol .fs-selected-pricing-amount-fraction,#root .fs-package .fs-selected-pricing-amount .fs-currency-symbol .fs-selected-pricing-amount-cycle,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer .fs-selected-pricing-amount-fraction,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer .fs-selected-pricing-amount-cycle,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container .fs-selected-pricing-amount-fraction,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container .fs-selected-pricing-amount-cycle,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-currency-symbol .fs-selected-pricing-amount-fraction,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-currency-symbol .fs-selected-pricing-amount-cycle,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer .fs-selected-pricing-amount-fraction,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer .fs-selected-pricing-amount-cycle,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container .fs-selected-pricing-amount-fraction,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container .fs-selected-pricing-amount-cycle{display:block;font-size:12px}#root .fs-package .fs-selected-pricing-amount .fs-currency-symbol .fs-selected-pricing-amount-fraction,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer .fs-selected-pricing-amount-fraction,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container .fs-selected-pricing-amount-fraction,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-currency-symbol .fs-selected-pricing-amount-fraction,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer .fs-selected-pricing-amount-fraction,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container .fs-selected-pricing-amount-fraction{vertical-align:top}#root .fs-package .fs-selected-pricing-amount .fs-currency-symbol .fs-selected-pricing-amount-cycle,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer .fs-selected-pricing-amount-cycle,#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container .fs-selected-pricing-amount-cycle,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-currency-symbol .fs-selected-pricing-amount-cycle,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-integer .fs-selected-pricing-amount-cycle,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container .fs-selected-pricing-amount-cycle{vertical-align:bottom}#root .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container,#fs_pricing_app .fs-package .fs-selected-pricing-amount .fs-selected-pricing-amount-fraction-container{color:var(--fs-ds-theme-muted-text-color)}#root .fs-package .fs-selected-pricing-amount-free,#fs_pricing_app .fs-package .fs-selected-pricing-amount-free{font-size:48px}#root .fs-package .fs-selected-pricing-cycle,#fs_pricing_app .fs-package .fs-selected-pricing-cycle{margin-bottom:5px;text-transform:uppercase;color:var(--fs-ds-theme-muted-text-color)}#root .fs-package .fs-selected-pricing-license-quantity,#fs_pricing_app .fs-package .fs-selected-pricing-license-quantity{color:var(--fs-ds-theme-muted-text-color)}#root .fs-package .fs-selected-pricing-license-quantity .fs-tooltip,#fs_pricing_app .fs-package .fs-selected-pricing-license-quantity .fs-tooltip{margin-left:5px}#root .fs-package .fs-upgrade-button-container,#fs_pricing_app .fs-package .fs-upgrade-button-container{padding:0 13px;display:block}#root .fs-package .fs-upgrade-button-container .fs-upgrade-button,#fs_pricing_app .fs-package .fs-upgrade-button-container .fs-upgrade-button{margin-top:20px;margin-bottom:5px}#root .fs-package .fs-plan-features,#fs_pricing_app .fs-package .fs-plan-features{text-align:left;margin-left:13px}#root .fs-package .fs-plan-features li,#fs_pricing_app .fs-package .fs-plan-features li{font-size:16px;display:flex;margin-bottom:8px}#root .fs-package .fs-plan-features li:not(:first-child),#fs_pricing_app .fs-package .fs-plan-features li:not(:first-child){margin-top:8px}#root .fs-package .fs-plan-features li>span,#root .fs-package .fs-plan-features li .fs-tooltip,#fs_pricing_app .fs-package .fs-plan-features li>span,#fs_pricing_app .fs-package .fs-plan-features li .fs-tooltip{font-size:small;vertical-align:middle;display:inline-block}#root .fs-package .fs-plan-features li .fs-feature-title,#fs_pricing_app .fs-package .fs-plan-features li .fs-feature-title{margin:0 5px;color:var(--fs-ds-theme-muted-text-color);max-width:260px;overflow-wrap:break-word}#root .fs-package .fs-support-and-main-features,#fs_pricing_app .fs-package .fs-support-and-main-features{margin-top:12px;padding-top:18px;padding-bottom:18px;color:var(--fs-ds-theme-muted-text-color)}#root .fs-package .fs-support-and-main-features .fs-plan-support,#fs_pricing_app .fs-package .fs-support-and-main-features .fs-plan-support{margin-bottom:15px}#root .fs-package .fs-support-and-main-features .fs-plan-features-with-value li,#fs_pricing_app .fs-package .fs-support-and-main-features .fs-plan-features-with-value li{font-size:small}#root .fs-package .fs-support-and-main-features .fs-plan-features-with-value li .fs-feature-title,#fs_pricing_app .fs-package .fs-support-and-main-features .fs-plan-features-with-value li .fs-feature-title{margin:0 2px}#root .fs-package .fs-support-and-main-features .fs-plan-features-with-value li:not(:first-child),#fs_pricing_app .fs-package .fs-support-and-main-features .fs-plan-features-with-value li:not(:first-child){margin-top:5px}#root .fs-package .fs-plan-features-with-value,#fs_pricing_app .fs-package .fs-plan-features-with-value{color:var(--fs-ds-theme-muted-text-color)}#root .fs-package .fs-license-quantities,#fs_pricing_app .fs-package .fs-license-quantities{border-collapse:collapse;position:relative;width:100%}#root .fs-package .fs-license-quantities,#root .fs-package .fs-license-quantities input,#fs_pricing_app .fs-package .fs-license-quantities,#fs_pricing_app .fs-package .fs-license-quantities input{cursor:pointer}#root .fs-package .fs-license-quantities .fs-license-quantity-discount span,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-discount span{background-color:var(--fs-ds-theme-background-color);border:1px solid var(--fs-ds-theme-primary-accent-color);color:var(--fs-ds-theme-primary-accent-color);display:inline;padding:4px 8px;border-radius:4px;font-weight:700;margin:0 5px;white-space:nowrap}#root .fs-package .fs-license-quantities .fs-license-quantity-discount span.fs-license-quantity-no-discount,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-discount span.fs-license-quantity-no-discount{visibility:hidden}#root .fs-package .fs-license-quantities .fs-license-quantity-container,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-container{line-height:30px;border-top:1px solid var(--fs-ds-theme-background-shade);font-size:small;color:var(--fs-ds-theme-muted-text-color)}#root .fs-package .fs-license-quantities .fs-license-quantity-container:last-child,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-container:last-child{border-bottom:1px solid var(--fs-ds-theme-background-shade)}#root .fs-package .fs-license-quantities .fs-license-quantity-container:last-child.fs-license-quantity-selected,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-container:last-child.fs-license-quantity-selected{border-bottom-color:var(--fs-ds-theme-divider-color)}#root .fs-package .fs-license-quantities .fs-license-quantity-container.fs-license-quantity-selected,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-container.fs-license-quantity-selected{background:var(--fs-ds-theme-background-shade);border-color:var(--fs-ds-theme-divider-color);color:var(--fs-ds-theme-text-color)}#root .fs-package .fs-license-quantities .fs-license-quantity-container.fs-license-quantity-selected+.fs-license-quantity-container,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-container.fs-license-quantity-selected+.fs-license-quantity-container{border-top-color:var(--fs-ds-theme-divider-color)}#root .fs-package .fs-license-quantities .fs-license-quantity-container>td:not(.fs-license-quantity-discount):not(.fs-license-quantity-price),#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-container>td:not(.fs-license-quantity-discount):not(.fs-license-quantity-price){text-align:left}#root .fs-package .fs-license-quantities .fs-license-quantity,#root .fs-package .fs-license-quantities .fs-license-quantity-discount,#root .fs-package .fs-license-quantities .fs-license-quantity-price,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-discount,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-price{vertical-align:middle}#root .fs-package .fs-license-quantities .fs-license-quantity,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity{position:relative;white-space:nowrap}#root .fs-package .fs-license-quantities .fs-license-quantity input,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity input{position:relative;margin-top:-1px;margin-left:7px;margin-right:7px}#root .fs-package .fs-license-quantities .fs-license-quantity-price,#fs_pricing_app .fs-package .fs-license-quantities .fs-license-quantity-price{position:relative;margin-right:auto;padding-right:7px;white-space:nowrap;font-variant-numeric:tabular-nums;text-align:right}#root .fs-package.fs-free-plan .fs-license-quantity-container:not(:last-child),#fs_pricing_app .fs-package.fs-free-plan .fs-license-quantity-container:not(:last-child){border-color:transparent}#root .fs-package .fs-most-popular,#fs_pricing_app .fs-package .fs-most-popular{display:none}#root .fs-package.fs-featured-plan .fs-most-popular,#fs_pricing_app .fs-package.fs-featured-plan .fs-most-popular{display:block;line-height:2.8em;margin-top:-2.8em;border-radius:10px 10px 0 0;color:var(--fs-ds-theme-text-color);background:var(--fs-ds-theme-package-popular-background);text-transform:uppercase;font-size:14px}#root .fs-package.fs-featured-plan .fs-plan-title,#fs_pricing_app .fs-package.fs-featured-plan .fs-plan-title{color:var(--fs-ds-theme-dark-background-text-color);background:var(--fs-ds-theme-primary-accent-color);border-top-color:var(--fs-ds-theme-primary-accent-color);border-bottom-color:var(--fs-ds-theme-primary-accent-color)}#root .fs-package.fs-featured-plan .fs-selected-pricing-license-quantity,#fs_pricing_app .fs-package.fs-featured-plan .fs-selected-pricing-license-quantity{color:var(--fs-ds-theme-primary-accent-color)}#root .fs-package.fs-featured-plan .fs-license-quantity-discount span,#fs_pricing_app .fs-package.fs-featured-plan .fs-license-quantity-discount span{background:var(--fs-ds-theme-primary-accent-color);color:var(--fs-ds-theme-dark-background-text-color)}#root .fs-package.fs-featured-plan .fs-license-quantities .fs-license-quantity-selected,#fs_pricing_app .fs-package.fs-featured-plan .fs-license-quantities .fs-license-quantity-selected{background:var(--fs-ds-theme-primary-accent-color);border-color:var(--fs-ds-theme-primary-accent-color);color:var(--fs-ds-theme-dark-background-text-color)}#root .fs-package.fs-featured-plan .fs-license-quantities .fs-license-quantity-selected+.fs-license-quantity-container,#fs_pricing_app .fs-package.fs-featured-plan .fs-license-quantities .fs-license-quantity-selected+.fs-license-quantity-container{border-top-color:var(--fs-ds-theme-primary-accent-color)}#root .fs-package.fs-featured-plan .fs-license-quantities .fs-license-quantity-selected:last-child,#fs_pricing_app .fs-package.fs-featured-plan .fs-license-quantities .fs-license-quantity-selected:last-child{border-bottom-color:var(--fs-ds-theme-primary-accent-color)}#root .fs-package.fs-featured-plan .fs-license-quantities .fs-license-quantity-selected .fs-license-quantity-discount span,#fs_pricing_app .fs-package.fs-featured-plan .fs-license-quantities .fs-license-quantity-selected .fs-license-quantity-discount span{background:var(--fs-ds-theme-background-color);color:var(--fs-ds-theme-primary-accent-color-hover)}\n',""]);const s=o},700:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var a=n(81),r=n.n(a),i=n(645),o=n.n(i)()(r());o.push([e.id,'#root .fs-section--packages,#fs_pricing_app .fs-section--packages{display:inline-block;width:100%;position:relative}#root .fs-section--packages .fs-packages-menu,#fs_pricing_app .fs-section--packages .fs-packages-menu{display:none;flex-wrap:wrap;justify-content:center}#root .fs-section--packages .fs-packages-tab,#fs_pricing_app .fs-section--packages .fs-packages-tab{display:none}#root .fs-section--packages .fs-package-tab,#fs_pricing_app .fs-section--packages .fs-package-tab{display:inline-block;flex:1}#root .fs-section--packages .fs-package-tab a,#fs_pricing_app .fs-section--packages .fs-package-tab a{display:block;padding:4px 10px 7px;border-bottom:2px solid transparent;color:#000;text-align:center;text-decoration:none}#root .fs-section--packages .fs-package-tab.fs-package-tab--selected a,#fs_pricing_app .fs-section--packages .fs-package-tab.fs-package-tab--selected a{border-color:#0085ba}#root .fs-section--packages .fs-packages-nav,#fs_pricing_app .fs-section--packages .fs-packages-nav{position:relative;overflow:hidden;margin:auto}#root .fs-section--packages .fs-packages-nav:before,#root .fs-section--packages .fs-packages-nav:after,#fs_pricing_app .fs-section--packages .fs-packages-nav:before,#fs_pricing_app .fs-section--packages .fs-packages-nav:after{position:absolute;top:0;bottom:0;width:60px;margin-bottom:32px}#root .fs-section--packages .fs-packages-nav:before,#fs_pricing_app .fs-section--packages .fs-packages-nav:before{z-index:1}#root .fs-section--packages .fs-packages-nav.fs-has-previous-plan:before,#fs_pricing_app .fs-section--packages .fs-packages-nav.fs-has-previous-plan:before{content:"";left:0;background:linear-gradient(to right,#cccccc96,transparent)}#root .fs-section--packages .fs-packages-nav.fs-has-next-plan:after,#fs_pricing_app .fs-section--packages .fs-packages-nav.fs-has-next-plan:after{content:"";right:0;background:linear-gradient(to left,#cccccc96,transparent)}#root .fs-section--packages .fs-packages-nav.fs-has-featured-plan:before,#root .fs-section--packages .fs-packages-nav.fs-has-featured-plan:after,#fs_pricing_app .fs-section--packages .fs-packages-nav.fs-has-featured-plan:before,#fs_pricing_app .fs-section--packages .fs-packages-nav.fs-has-featured-plan:after{top:2.8em}#root .fs-section--packages .fs-prev-package,#root .fs-section--packages .fs-next-package,#fs_pricing_app .fs-section--packages .fs-prev-package,#fs_pricing_app .fs-section--packages .fs-next-package{position:absolute;top:50%;margin-top:-11px;cursor:pointer;font-size:48px;z-index:1}#root .fs-section--packages .fs-prev-package,#fs_pricing_app .fs-section--packages .fs-prev-package{visibility:hidden;z-index:2}#root .fs-section--packages .fs-has-featured-plan .fs-packages,#fs_pricing_app .fs-section--packages .fs-has-featured-plan .fs-packages{margin-top:2.8em}#root .fs-section--packages .fs-packages,#fs_pricing_app .fs-section--packages .fs-packages{width:auto;display:flex;flex-direction:row;margin-left:auto;margin-right:auto;margin-bottom:30px;border-top-right-radius:10px;position:relative;transition:left .5s ease,right .5s ease;padding-top:5px}#root .fs-section--packages .fs-packages:before,#fs_pricing_app .fs-section--packages .fs-packages:before{content:"";position:absolute;top:0;right:0;bottom:0;width:100px;height:100px}@media only screen and (max-width: 768px){#root .fs-section--plans-and-pricing .fs-section--packages .fs-next-package,#root .fs-section--plans-and-pricing .fs-section--packages .fs-prev-package,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-next-package,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-prev-package{display:none}#root .fs-section--plans-and-pricing .fs-section--packages .fs-packages-menu,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-packages-menu{display:block;font-size:24px;margin:0 auto 10px}#root .fs-section--plans-and-pricing .fs-section--packages .fs-packages-tab,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-packages-tab{display:flex;font-size:18px;margin:0 auto 10px}#root .fs-section--plans-and-pricing .fs-section--packages .fs-packages .fs-most-popular,#root .fs-section--plans-and-pricing .fs-section--packages .fs-package .fs-most-popular,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-packages .fs-most-popular,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-package .fs-most-popular{display:none}#root .fs-section--plans-and-pricing .fs-section--packages .fs-has-featured-plan .fs-packages,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-has-featured-plan .fs-packages{margin-top:0}}@media only screen and (max-width: 455px){#root .fs-section--plans-and-pricing .fs-section--packages .fs-packages .fs-package,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-packages .fs-package{width:100%}#root .fs-section--plans-and-pricing,#fs_pricing_app .fs-section--plans-and-pricing{padding:10px}}@media only screen and (max-width: 375px){#root .fs-section--plans-and-pricing .fs-section--packages .fs-packages .fs-package,#fs_pricing_app .fs-section--plans-and-pricing .fs-section--packages .fs-packages .fs-package{width:100%}}\n',""]);const s=o},302:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var a=n(81),r=n.n(a),i=n(645),o=n.n(i)()(r());o.push([e.id,'#root .fs-tooltip,#fs_pricing_app .fs-tooltip{cursor:help;position:relative;color:inherit}#root .fs-tooltip .fs-tooltip-message,#fs_pricing_app .fs-tooltip .fs-tooltip-message{position:absolute;width:200px;background:var(--fs-ds-theme-background-darkest);z-index:1;display:none;border-radius:4px;color:var(--fs-ds-theme-dark-background-text-color);padding:8px;text-align:left;line-height:18px}#root .fs-tooltip .fs-tooltip-message:before,#fs_pricing_app .fs-tooltip .fs-tooltip-message:before{content:"";position:absolute;z-index:1}#root .fs-tooltip .fs-tooltip-message:not(.fs-tooltip-message--position-none),#fs_pricing_app .fs-tooltip .fs-tooltip-message:not(.fs-tooltip-message--position-none){display:block}#root .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-right,#fs_pricing_app .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-right{transform:translateY(-50%);left:30px;top:8px}#root .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-right:before,#fs_pricing_app .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-right:before{left:-8px;top:50%;margin-top:-6px;border-top:6px solid transparent;border-bottom:6px solid transparent;border-right:8px solid var(--fs-ds-theme-background-darkest)}#root .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-top,#fs_pricing_app .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-top{left:50%;bottom:30px;transform:translate(-50%)}#root .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-top:before,#fs_pricing_app .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-top:before{left:50%;bottom:-8px;margin-left:-6px;border-right:6px solid transparent;border-left:6px solid transparent;border-top:8px solid var(--fs-ds-theme-background-darkest)}#root .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-top-right,#fs_pricing_app .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-top-right{right:-10px;bottom:30px}#root .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-top-right:before,#fs_pricing_app .fs-tooltip .fs-tooltip-message.fs-tooltip-message--position-top-right:before{right:10px;bottom:-8px;margin-left:-6px;border-right:6px solid transparent;border-left:6px solid transparent;border-top:8px solid var(--fs-ds-theme-background-darkest)}\n',""]);const s=o},645:e=>{"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n="",a=void 0!==t[5];return t[4]&&(n+="@supports (".concat(t[4],") {")),t[2]&&(n+="@media ".concat(t[2]," {")),a&&(n+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),n+=e(t),a&&(n+="}"),t[2]&&(n+="}"),t[4]&&(n+="}"),n})).join("")},t.i=function(e,n,a,r,i){"string"==typeof e&&(e=[[null,e,void 0]]);var o={};if(a)for(var s=0;s0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=i),n&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=n):u[2]=n),r&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=r):u[4]="".concat(r)),t.push(u))}},t}},81:e=>{"use strict";e.exports=function(e){return e[1]}},867:(e,t,n)=>{let a=document.getElementById("fs_pricing_wrapper");a&&a.dataset&&a.dataset.publicUrl&&(n.p=a.dataset.publicUrl)},738:e=>{function t(e){return!!e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}e.exports=function(e){return null!=e&&(t(e)||function(e){return"function"==typeof e.readFloatLE&&"function"==typeof e.slice&&t(e.slice(0,0))}(e)||!!e._isBuffer)}},568:(e,t,n)=>{var a,r,i,o,s;a=n(12),r=n(487).utf8,i=n(738),o=n(487).bin,(s=function(e,t){e.constructor==String?e=t&&"binary"===t.encoding?o.stringToBytes(e):r.stringToBytes(e):i(e)?e=Array.prototype.slice.call(e,0):Array.isArray(e)||e.constructor===Uint8Array||(e=e.toString());for(var n=a.bytesToWords(e),l=8*e.length,c=1732584193,u=-271733879,f=-1732584194,p=271733878,d=0;d>>24)|4278255360&(n[d]<<24|n[d]>>>8);n[l>>>5]|=128<>>9<<4)]=l;var m=s._ff,g=s._gg,h=s._hh,b=s._ii;for(d=0;d>>0,u=u+v>>>0,f=f+k>>>0,p=p+_>>>0}return a.endian([c,u,f,p])})._ff=function(e,t,n,a,r,i,o){var s=e+(t&n|~t&a)+(r>>>0)+o;return(s<>>32-i)+t},s._gg=function(e,t,n,a,r,i,o){var s=e+(t&a|n&~a)+(r>>>0)+o;return(s<>>32-i)+t},s._hh=function(e,t,n,a,r,i,o){var s=e+(t^n^a)+(r>>>0)+o;return(s<>>32-i)+t},s._ii=function(e,t,n,a,r,i,o){var s=e+(n^(t|~a))+(r>>>0)+o;return(s<>>32-i)+t},s._blocksize=16,s._digestsize=16,e.exports=function(e,t){if(null==e)throw new Error("Illegal argument "+e);var n=a.wordsToBytes(s(e,t));return t&&t.asBytes?n:t&&t.asString?o.bytesToString(n):a.bytesToHex(n)}},418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;function r(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var a={};return"abcdefghijklmnopqrst".split("").forEach((function(e){a[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},a)).join("")}catch(e){return!1}}()?Object.assign:function(e,i){for(var o,s,l=r(e),c=1;c{"use strict";var a=n(414);function r(){}function i(){}i.resetWarningCache=r,e.exports=function(){function e(e,t,n,r,i,o){if(o!==a){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:i,resetWarningCache:r};return n.PropTypes=n,n}},697:(e,t,n)=>{e.exports=n(703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},448:(e,t,n)=>{"use strict";var a=n(294),r=n(418),i=n(840);function o(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n register_section_type( 'FS_Customizer_Support_Section' ); parent::__construct( $manager, $id, $args ); } /** * The type of customize section being rendered. * * @since 1.0.0 * @access public * @var string */ public $type = 'freemius-support-section'; /** * @var Freemius */ public $fs = null; /** * Add custom parameters to pass to the JS via JSON. * * @since 1.0.0 */ public function json() { $json = parent::json(); $is_contact_visible = $this->fs->is_page_visible( 'contact' ); $is_support_visible = $this->fs->is_page_visible( 'support' ); $json['theme_title'] = $this->fs->get_plugin_name(); if ( $is_contact_visible && $is_support_visible ) { $json['theme_title'] .= ' ' . $this->fs->get_text_inline( 'Support', 'support' ); } if ( $is_contact_visible ) { $json['contact'] = array( 'label' => $this->fs->get_text_inline( 'Contact Us', 'contact-us' ), 'url' => $this->fs->contact_url(), ); } if ( $is_support_visible ) { $json['support'] = array( 'label' => $this->fs->get_text_inline( 'Support Forum', 'support-forum' ), 'url' => $this->fs->get_support_forum_url() ); } return $json; } /** * Outputs the Underscore.js template. * * @since 1.0.0 */ protected function render_template() { ?>
  • {{ data.theme_title }} <# if ( data.contact && data.support ) { #>
    <# } #> <# if ( data.contact ) { #> {{ data.contact.label }} <# } #> <# if ( data.support ) { #> {{ data.support.label }} <# } #> <# if ( data.contact && data.support ) { #>
    <# } #>

  • title( 'Freemius' ); // @phpstan-ignore-line } public static function requests_count() { if ( class_exists( 'Freemius_Api_WordPress' ) ) { $logger = Freemius_Api_WordPress::GetLogger(); } else { $logger = array(); } return number_format( count( $logger ) ); } public static function total_time() { if ( class_exists( 'Freemius_Api_WordPress' ) ) { $logger = Freemius_Api_WordPress::GetLogger(); } else { $logger = array(); } $total_time = .0; foreach ( $logger as $l ) { $total_time += $l['total']; } return number_format( 100 * $total_time, 2 ) . ' ' . fs_text_x_inline( 'ms', 'milliseconds' ); } public function render() { ?>



    first ) ? $this->first : '' ) ) . ' ' . ucfirst( trim( is_string( $this->last ) ? $this->last : '' ) ) ); } function is_verified() { return ( isset( $this->is_verified ) && true === $this->is_verified ); } /** * @author Leo Fajardo (@leorw) * @since 2.4.2 * * @return bool */ function is_beta() { // Return `false` since this is just for backward compatibility. return false; } static function get_type() { return 'user'; } }freemius/includes/entities/class-fs-subscription.php000064400000006540147600046700016774 0ustar00is_canceled() ) { return false; } return ( ! empty( $this->next_payment ) && strtotime( $this->next_payment ) > WP_FS__SCRIPT_START_TIME ); } /** * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @return bool */ function is_canceled() { return ! is_null( $this->canceled_at ); } /** * Subscription considered to be new without any payments * if the next payment should be made within less than 24 hours * from the subscription creation. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_first_payment_pending() { return ( WP_FS__TIME_24_HOURS_IN_SEC >= strtotime( $this->next_payment ) - strtotime( $this->created ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7 */ function has_trial() { return ! is_null( $this->trial_ends ); } }freemius/includes/entities/class-fs-site.php000064400000020746147600046700015220 0ustar00plan_id = $site->plan_id; } if ( ! is_bool( $this->is_disconnected ) ) { $this->is_disconnected = false; } } static function get_type() { return 'install'; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $url * * @return bool */ static function is_localhost_by_address( $url ) { if ( false !== strpos( $url, '127.0.0.1' ) || false !== strpos( $url, 'localhost' ) ) { return true; } if ( ! fs_starts_with( $url, 'http' ) ) { $url = 'http://' . $url; } $url_parts = parse_url( $url ); $subdomain = $url_parts['host']; return ( // Starts with. fs_starts_with( $subdomain, 'local.' ) || fs_starts_with( $subdomain, 'dev.' ) || fs_starts_with( $subdomain, 'test.' ) || fs_starts_with( $subdomain, 'stage.' ) || fs_starts_with( $subdomain, 'staging.' ) || // Ends with. fs_ends_with( $subdomain, '.dev' ) || fs_ends_with( $subdomain, '.test' ) || fs_ends_with( $subdomain, '.staging' ) || fs_ends_with( $subdomain, '.local' ) || fs_ends_with( $subdomain, '.example' ) || fs_ends_with( $subdomain, '.invalid' ) || // GoDaddy test/dev. fs_ends_with( $subdomain, '.myftpupload.com' ) || // ngrok tunneling. fs_ends_with( $subdomain, '.ngrok.io' ) || // wpsandbox. fs_ends_with( $subdomain, '.wpsandbox.pro' ) || // SiteGround staging. fs_starts_with( $subdomain, 'staging' ) || // WPEngine staging. fs_ends_with( $subdomain, '.staging.wpengine.com' ) || fs_ends_with( $subdomain, '.dev.wpengine.com' ) || fs_ends_with( $subdomain, '.wpengine.com' ) || fs_ends_with( $subdomain, '.wpenginepowered.com' ) || // Pantheon ( fs_ends_with( $subdomain, 'pantheonsite.io' ) && ( fs_starts_with( $subdomain, 'test-' ) || fs_starts_with( $subdomain, 'dev-' ) ) ) || // Cloudways fs_ends_with( $subdomain, '.cloudwaysapps.com' ) || // Kinsta ( ( fs_starts_with( $subdomain, 'stg-' ) || fs_starts_with( $subdomain, 'staging-' ) || fs_starts_with( $subdomain, 'env-' ) ) && ( fs_ends_with( $subdomain, '.kinsta.com' ) || fs_ends_with( $subdomain, '.kinsta.cloud' ) ) ) || // DesktopServer fs_ends_with( $subdomain, '.dev.cc' ) || // Pressable fs_ends_with( $subdomain, '.mystagingwebsite.com' ) || // WPMU DEV ( fs_ends_with( $subdomain, '.tempurl.host' ) || fs_ends_with( $subdomain, '.wpmudev.host' ) ) || // Vendasta ( fs_ends_with( $subdomain, '.websitepro-staging.com' ) || fs_ends_with( $subdomain, '.websitepro.hosting' ) ) || // InstaWP fs_ends_with( $subdomain, '.instawp.xyz' ) || // 10Web Hosting ( fs_ends_with( $subdomain, '-dev.10web.site' ) || fs_ends_with( $subdomain, '-dev.10web.cloud' ) ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.9.1 * * @param string $host * * @return bool */ static function is_playground_wp_environment_by_host( $host ) { // Services aimed at providing a WordPress sandbox environment. $sandbox_wp_environment_domains = array( // InstaWP 'instawp.xyz', // TasteWP 'tastewp.com', // WordPress Playground 'playground.wordpress.net', ); foreach ( $sandbox_wp_environment_domains as $domain) { if ( ( $host === $domain ) || fs_ends_with( $host, '.' . $domain ) || fs_ends_with( $host, '-' . $domain ) ) { return true; } } return false; } function is_localhost() { return ( WP_FS__IS_LOCALHOST_FOR_SERVER || self::is_localhost_by_address( $this->url ) ); } /** * Check if site in trial. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_trial() { return is_numeric( $this->trial_plan_id ) && ( strtotime( $this->trial_ends ) > WP_FS__SCRIPT_START_TIME ); } /** * Check if user already utilized the trial with the current install. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_trial_utilized() { return is_numeric( $this->trial_plan_id ); } /** * @author Edgar Melkonyan * * @return bool */ function is_beta() { return ( isset( $this->is_beta ) && true === $this->is_beta ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.1 * * @param string $site_url * * @return bool */ function is_clone( $site_url ) { $clone_install_url = trailingslashit( fs_strip_url_protocol( $this->url ) ); return ( $clone_install_url !== $site_url ); } }freemius/includes/entities/class-fs-scope-entity.php000064400000001052147600046700016664 0ustar00monthly_price ) && $this->monthly_price > 0 ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @return bool */ function has_annual() { return ( is_numeric( $this->annual_price ) && $this->annual_price > 0 ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @return bool */ function has_lifetime() { return ( is_numeric( $this->lifetime_price ) && $this->lifetime_price > 0 ); } /** * Check if unlimited licenses pricing. * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @return bool */ function is_unlimited() { return is_null( $this->licenses ); } /** * Check if pricing has more than one billing cycle. * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @return bool */ function is_multi_cycle() { $cycles = 0; if ( $this->has_monthly() ) { $cycles ++; } if ( $this->has_annual() ) { $cycles ++; } if ( $this->has_lifetime() ) { $cycles ++; } return $cycles > 1; } /** * Get annual over monthly discount. * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @return int */ function annual_discount_percentage() { return floor( $this->annual_savings() / ( $this->monthly_price * 12 * ( $this->is_unlimited() ? 1 : $this->licenses ) ) * 100 ); } /** * Get annual over monthly savings. * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @return float */ function annual_savings() { return ( $this->monthly_price * 12 - $this->annual_price ) * ( $this->is_unlimited() ? 1 : $this->licenses ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @return bool */ function is_usd() { return ( 'usd' === $this->currency ); } }freemius/includes/entities/class-fs-plugin-tag.php000064400000002451147600046700016314 0ustar00release_mode ); } }freemius/includes/entities/class-fs-plugin-plan.php000064400000005556147600046700016504 0ustar00name = strtolower( $plan->name ); } } static function get_type() { return 'plan'; } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_free() { return ( 'free' === $this->name ); } /** * Checks if this plan supports "Technical Support". * * @author Leo Fajardo (leorw) * @since 1.2.0 * * @return bool */ function has_technical_support() { return ( ! empty( $this->support_email ) || ! empty( $this->support_skype ) || ! empty( $this->support_phone ) || ! empty( $this->is_success_manager ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function has_trial() { return ! $this->is_free() && is_numeric( $this->trial_period ) && ( $this->trial_period > 0 ); } }freemius/includes/entities/class-fs-plugin.php000064400000007566147600046700015557 0ustar00is_premium = false; $this->is_live = true; if ( empty( $this->premium_slug ) && ! empty( $plugin->slug ) ) { $this->premium_slug = "{$this->slug}-premium"; } if ( empty( $this->premium_suffix ) ) { $this->premium_suffix = '(Premium)'; } if ( isset( $plugin->info ) && is_object( $plugin->info ) ) { $this->info = new FS_Plugin_Info( $plugin->info ); } } /** * Check if plugin is an add-on (has parent). * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool */ function is_addon() { return isset( $this->parent_plugin_id ) && is_numeric( $this->parent_plugin_id ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 * * @return bool */ function has_affiliate_program() { return ( ! empty( $this->affiliate_moderation ) ); } static function get_type() { return 'plugin'; } }freemius/includes/entities/class-fs-plugin-license.php000064400000021110147600046700017154 0ustar00is_features_enabled() ) { return 0; } if ( $this->is_unlimited() ) { return 999; } return ( $this->quota - $this->activated - ( $this->is_free_localhost ? 0 : $this->activated_local ) ); } /** * Check if single site license. * * @author Vova Feldman (@svovaf) * @since 1.1.8.1 * * @return bool */ function is_single_site() { return ( is_numeric( $this->quota ) && 1 == $this->quota ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @return bool */ function is_expired() { return ! $this->is_lifetime() && ( strtotime( $this->expiration ) < WP_FS__SCRIPT_START_TIME ); } /** * Check if license is not expired. * * @author Vova Feldman (@svovaf) * @since 1.2.1 * * @return bool */ function is_valid() { return ! $this->is_expired(); } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool */ function is_lifetime() { return is_null( $this->expiration ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.0 * * @return bool */ function is_unlimited() { return is_null( $this->quota ); } /** * Check if license is fully utilized. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param bool|null $is_localhost * * @return bool */ function is_utilized( $is_localhost = null ) { if ( is_null( $is_localhost ) ) { $is_localhost = WP_FS__IS_LOCALHOST_FOR_SERVER; } if ( $this->is_unlimited() ) { return false; } return ! ( $this->is_free_localhost && $is_localhost ) && ( $this->quota <= $this->activated + ( $this->is_free_localhost ? 0 : $this->activated_local ) ); } /** * Check if license can be activated. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param bool|null $is_localhost * * @return bool */ function can_activate( $is_localhost = null ) { return ! $this->is_utilized( $is_localhost ) && $this->is_features_enabled(); } /** * Check if license can be activated on a given number of production and localhost sites. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $production_count * @param int $localhost_count * * @return bool */ function can_activate_bulk( $production_count, $localhost_count ) { if ( $this->is_unlimited() ) { return true; } /** * For simplicity, the logic will work as following: when given X sites to activate the license on, if it's * possible to activate on ALL of them, do the activation. If it's not possible to activate on ALL of them, * do NOT activate on any of them. */ return ( $this->quota >= $this->activated + $production_count + ( $this->is_free_localhost ? 0 : $this->activated_local + $localhost_count ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1 * * @return bool */ function is_active() { return ( ! $this->is_cancelled ); } /** * Check if license's plan features are enabled. * * - Either if plan not expired * - If expired, based on the configuration to block features or not. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool */ function is_features_enabled() { return $this->is_active() && ( ! $this->is_block_features || ! $this->is_expired() ); } /** * Subscription considered to be new without any payments * if the license expires in less than 24 hours * from the license creation. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_first_payment_pending() { if ( $this->is_lifetime() ) { return false; } return ( WP_FS__TIME_24_HOURS_IN_SEC >= strtotime( $this->expiration ) - strtotime( $this->created ) ); } /** * @return int */ function total_activations() { return ( $this->activated + $this->activated_local ); } /** * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @return string */ function get_html_escaped_masked_secret_key() { return self::mask_secret_key_for_html( $this->secret_key ); } /** * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @param string $secret_key * * @return string */ static function mask_secret_key_for_html( $secret_key ) { return ( // Initial 6 chars - sk_ABC htmlspecialchars( substr( $secret_key, 0, 6 ) ) . // Masking str_pad( '', ( strlen( $secret_key ) - 9 ) * 6, '•' ) . // Last 3 chars. htmlspecialchars( substr( $secret_key, - 3 ) ) ); } } freemius/includes/entities/class-fs-plugin-info.php000064400000001332147600046700016471 0ustar00bound_payment_id ) && 0 > $this->gross ); } /** * Checks if the payment was migrated from another platform. * * @author Vova Feldman (@svovaf) * @since 2.0.2 * * @return bool */ function is_migrated() { return ( 0 != $this->source ); } /** * Returns the gross in this format: * `{symbol}{amount | 2 decimal digits} {currency | uppercase}` * * Examples: £9.99 GBP, -£9.99 GBP. * * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @return string */ function formatted_gross() { return ( ( $this->gross < 0 ? '-' : '' ) . $this->get_symbol() . number_format( abs( $this->gross ), 2, '.', ',' ) . ' ' . strtoupper( $this->currency ) ); } /** * A map between supported currencies with their symbols. * * @var array */ static $CURRENCY_2_SYMBOL; /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @return string */ private function get_symbol() { if ( ! isset( self::$CURRENCY_2_SYMBOL ) ) { // Lazy load. self::$CURRENCY_2_SYMBOL = array( self::CURRENCY_USD => '$', self::CURRENCY_GBP => '£', self::CURRENCY_EUR => '€', ); } return self::$CURRENCY_2_SYMBOL[ $this->currency ]; } }freemius/includes/entities/class-fs-entity.php000064400000006066147600046700015567 0ustar00 $def_value ) { $this->{$key} = isset( $entity->{$key} ) ? $entity->{$key} : $def_value; } } static function get_type() { return 'type'; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param FS_Entity $entity1 * @param FS_Entity $entity2 * * @return bool */ static function equals( $entity1, $entity2 ) { if ( is_null( $entity1 ) && is_null( $entity2 ) ) { return true; } else if ( is_object( $entity1 ) && is_object( $entity2 ) ) { return ( $entity1->id == $entity2->id ); } else if ( is_object( $entity1 ) ) { return is_null( $entity1->id ); } else { return is_null( $entity2->id ); } } private $_is_updated = false; /** * Update object property. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string|array[string]mixed $key * @param string|bool $val * * @return bool */ function update( $key, $val = false ) { if ( ! is_array( $key ) ) { $key = array( $key => $val ); } $is_updated = false; foreach ( $key as $k => $v ) { if ( $this->{$k} === $v ) { continue; } if ( ( is_string( $this->{$k} ) && is_numeric( $v ) || ( is_numeric( $this->{$k} ) && is_string( $v ) ) ) && $this->{$k} == $v ) { continue; } // Update value. $this->{$k} = $v; $is_updated = true; } $this->_is_updated = $is_updated; return $is_updated; } /** * Checks if entity was updated. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_updated() { return $this->_is_updated; } /** * @param $id * * @author Vova Feldman (@svovaf) * @since 1.1.2 * * @return bool */ static function is_valid_id($id){ return is_numeric($id); } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @return string */ public static function get_class_name() { return get_called_class(); } }freemius/includes/entities/class-fs-billing.php000064400000002750147600046700015667 0ustar00commission_type ) ? ( '$' . $this->commission ) : ( $this->commission . '%' ); } /** * @author Leo Fajardo (@leorw) * * @return bool */ function has_lifetime_commission() { return ( 0 !== $this->future_payments_days ); } /** * @author Leo Fajardo (@leorw) * * @return bool */ function is_session_cookie() { return ( 0 == $this->cookie_days ); } /** * @author Leo Fajardo (@leorw) * * @return bool */ function has_renewals_commission() { return ( is_null( $this->commission_renewals_days ) || $this->commission_renewals_days > 0 ); } }freemius/includes/entities/class-fs-affiliate.php000064400000003361147600046700016172 0ustar00status ); } /** * @author Leo Fajardo * * @return bool */ function is_pending() { return ( 'pending' === $this->status ); } /** * @author Leo Fajardo * * @return bool */ function is_suspended() { return ( 'suspended' === $this->status ); } /** * @author Leo Fajardo * * @return bool */ function is_rejected() { return ( 'rejected' === $this->status ); } /** * @author Leo Fajardo * * @return bool */ function is_blocked() { return ( 'blocked' === $this->status ); } }freemius/includes/managers/index.php000064400000000127147600046700013612 0ustar00_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_' . 'plugins', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); $this->_module_id = $module_id; $this->load(); } protected function get_option_manager() { return FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true, true ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @param string|bool $module_type "plugin", "theme", or "false" for all modules. * * @return array */ protected function get_all_modules( $module_type = false ) { $option_manager = $this->get_option_manager(); if ( false !== $module_type ) { return fs_get_entities( $option_manager->get_option( $module_type . 's', array() ), FS_Plugin::get_class_name() ); } return array( self::OPTION_NAME_PLUGINS => fs_get_entities( $option_manager->get_option( self::OPTION_NAME_PLUGINS, array() ), FS_Plugin::get_class_name() ), self::OPTION_NAME_THEMES => fs_get_entities( $option_manager->get_option( self::OPTION_NAME_THEMES, array() ), FS_Plugin::get_class_name() ), ); } /** * Load plugin data from local DB. * * @author Vova Feldman (@svovaf) * @since 1.0.6 */ function load() { $all_modules = $this->get_all_modules(); if ( ! is_numeric( $this->_module_id ) ) { unset( $all_modules[ self::OPTION_NAME_THEMES ] ); } foreach ( $all_modules as $modules ) { /** * @since 1.2.2 * * @var $modules FS_Plugin[] */ foreach ( $modules as $module ) { $found_module = false; /** * If module ID is not numeric, it must be a plugin's slug. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ if ( ! is_numeric( $this->_module_id ) ) { if ( $this->_module_id === $module->slug ) { $this->_module_id = $module->id; $found_module = true; } } else if ( $this->_module_id == $module->id ) { $found_module = true; } if ( $found_module ) { $this->_module = $module; break; } } } } /** * Store plugin on local DB. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param bool|FS_Plugin $module * @param bool $flush * * @return bool|\FS_Plugin */ function store( $module = false, $flush = true ) { if ( false !== $module ) { $this->_module = $module; } $all_modules = $this->get_all_modules( $this->_module->type ); $all_modules[ $this->_module->slug ] = $this->_module; $options_manager = $this->get_option_manager(); $options_manager->set_option( $this->_module->type . 's', $all_modules, $flush ); return $this->_module; } /** * Update local plugin data if different. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param \FS_Plugin $plugin * @param bool $store * * @return bool True if plugin was updated. */ function update( FS_Plugin $plugin, $store = true ) { if ( ! ($this->_module instanceof FS_Plugin ) || $this->_module->slug != $plugin->slug || $this->_module->public_key != $plugin->public_key || $this->_module->secret_key != $plugin->secret_key || $this->_module->parent_plugin_id != $plugin->parent_plugin_id || $this->_module->title != $plugin->title ) { $this->store( $plugin, $store ); return true; } return false; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param FS_Plugin $plugin * @param bool $store */ function set( FS_Plugin $plugin, $store = false ) { $this->_module = $plugin; if ( $store ) { $this->store(); } } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool|\FS_Plugin */ function get() { if ( isset( $this->_module ) ) { return $this->_module; } if ( empty( $this->_module_id ) ) { return false; } /** * Return an FS_Plugin entity that has its `id` and `is_live` properties set (`is_live` is initialized in the FS_Plugin constructor) to avoid triggering an error that is relevant to these properties when the FS_Plugin entity is used before the `parse_settings()` method is called. This can happen when creating a regular WordPress site by cloning a subsite of a multisite network and the data that is stored in the network-level storage is not cloned. * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ $plugin = new FS_Plugin(); $plugin->id = $this->_module_id; return $plugin; } }freemius/includes/managers/class-fs-plan-manager.php000064400000007570147600046700016567 0ustar00is_utilized() && $license->is_features_enabled() ) { return true; } } } return false; } /** * Check if plugin has any paid plans. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param FS_Plugin_Plan[] $plans * * @return bool */ function has_paid_plan( $plans ) { if ( ! is_array( $plans ) || 0 === count( $plans ) ) { return false; } /** * @var FS_Plugin_Plan[] $plans */ for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) { if ( ! $plans[ $i ]->is_free() ) { return true; } } return false; } /** * Check if plugin has any free plan, or is it premium only. * * Note: If no plans configured, assume plugin is free. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param FS_Plugin_Plan[] $plans * * @return bool */ function has_free_plan( $plans ) { if ( ! is_array( $plans ) || 0 === count( $plans ) ) { return true; } /** * @var FS_Plugin_Plan[] $plans */ for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) { if ( $plans[ $i ]->is_free() ) { return true; } } return false; } /** * Find all plans that have trial. * Since 2.6.2 call get_filtered_plan * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param FS_Plugin_Plan[] $plans * * @return FS_Plugin_Plan[] */ function get_trial_plans( $plans ) { return $this->get_filtered_plans( $plans, true ); } /** * Find all plans that are not hidden and have trial. * * @author Daniele Alessandra (@danielealessandra) * * @param FS_Plugin_Plan[] $plans * * @return FS_Plugin_Plan[] * @since 2.6.3 * */ function get_visible_trial_plans( $plans ) { return $this->get_filtered_plans( $plans, true, true ); } /** * Find all plans filtered by trial or visibility. * * @author Daniele Alessandra (@danielealessandra) * * @param FS_Plugin_Plan[] $plans * @param boolean $should_have_trials * @param boolean $should_be_visible * * @return FS_Plugin_Plan[] * @since 2.6.3 * */ function get_filtered_plans( $plans, $should_have_trials = false, $should_be_visible = false ) { $filtered_plans = array(); if ( is_array( $plans ) && count( $plans ) > 0 ) { foreach ( $plans as $plan ) { if ( ( $should_have_trials && ! $plan->has_trial() ) || ( $should_be_visible && $plan->is_hidden ) ) { continue; } $filtered_plans[] = $plan; } } return $filtered_plans; } /** * Check if plugin has any trial plan. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param FS_Plugin_Plan[] $plans * * @return bool */ function has_trial_plan( $plans ) { if ( ! is_array( $plans ) || 0 === count( $plans ) ) { return true; } /** * @var FS_Plugin_Plan[] $plans */ for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) { if ( $plans[ $i ]->has_trial() ) { return true; } } return false; } }freemius/includes/managers/class-fs-permission-manager.php000064400000060666147600046700020032 0ustar00 */ private static $_instances = array(); const PERMISSION_USER = 'user'; const PERMISSION_SITE = 'site'; const PERMISSION_EVENTS = 'events'; const PERMISSION_ESSENTIALS = 'essentials'; const PERMISSION_DIAGNOSTIC = 'diagnostic'; const PERMISSION_EXTENSIONS = 'extensions'; const PERMISSION_NEWSLETTER = 'newsletter'; /** * @param Freemius $fs * * @return self */ static function instance( Freemius $fs ) { $id = $fs->get_id(); if ( ! isset( self::$_instances[ $id ] ) ) { self::$_instances[ $id ] = new self( $fs ); } return self::$_instances[ $id ]; } /** * @param Freemius $fs */ protected function __construct( Freemius $fs ) { $this->_fs = $fs; $this->_storage = FS_Storage::instance( $fs->get_module_type(), $fs->get_slug() ); } /** * @return string[] */ static function get_all_permission_ids() { return array( self::PERMISSION_USER, self::PERMISSION_SITE, self::PERMISSION_EVENTS, self::PERMISSION_ESSENTIALS, self::PERMISSION_DIAGNOSTIC, self::PERMISSION_EXTENSIONS, self::PERMISSION_NEWSLETTER, ); } /** * @return string[] */ static function get_api_managed_permission_ids() { return array( self::PERMISSION_USER, self::PERMISSION_SITE, self::PERMISSION_EXTENSIONS, ); } /** * @param string $permission * * @return bool */ static function is_supported_permission( $permission ) { return in_array( $permission, self::get_all_permission_ids() ); } /** * @since 2.5.3 * * @return bool */ function is_premium_context() { return ( $this->_fs->is_premium() || $this->_fs->can_use_premium_code() ); } /** * @param bool $is_license_activation * @param array[] $extra_permissions * * @return array[] */ function get_permissions( $is_license_activation, array $extra_permissions = array() ) { return $is_license_activation ? $this->get_license_activation_permissions( $extra_permissions ) : $this->get_opt_in_permissions( $extra_permissions ); } #-------------------------------------------------------------------------------- #region Opt-In Permissions #-------------------------------------------------------------------------------- /** * @param array[] $extra_permissions * * @return array[] */ function get_opt_in_permissions( array $extra_permissions = array(), $load_default_from_storage = false, $is_optional = false ) { $permissions = array_merge( $this->get_opt_in_required_permissions( $load_default_from_storage ), $this->get_opt_in_optional_permissions( $load_default_from_storage, $is_optional ), $extra_permissions ); return $this->get_sorted_permissions_by_priority( $permissions ); } /** * @param bool $load_default_from_storage * * @return array[] */ function get_opt_in_required_permissions( $load_default_from_storage = false ) { return array( $this->get_user_permission( $load_default_from_storage ) ); } /** * @param bool $load_default_from_storage * @param bool $is_optional * * @return array[] */ function get_opt_in_optional_permissions( $load_default_from_storage = false, $is_optional = false ) { return array_merge( $this->get_opt_in_diagnostic_permissions( $load_default_from_storage, $is_optional ), array( $this->get_extensions_permission( false, false, $load_default_from_storage ) ) ); } /** * @param bool $load_default_from_storage * @param bool $is_optional * * @return array[] */ function get_opt_in_diagnostic_permissions( $load_default_from_storage = false, $is_optional = false ) { // Alias. $fs = $this->_fs; $permissions = array(); $permissions[] = $this->get_permission( self::PERMISSION_SITE, 'admin-links', $fs->get_text_inline( 'View Basic Website Info', 'permissions-site' ), $fs->get_text_inline( 'Homepage URL & title, WP & PHP versions, and site language', 'permissions-site_desc' ), sprintf( /* translators: %s: 'Plugin' or 'Theme' */ $fs->get_text_inline( 'To provide additional functionality that\'s relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the %s should be translated and tailored to.', 'permissions-site_tooltip' ), $fs->get_module_label( true ) ), 10, $is_optional, true, $load_default_from_storage ); $permissions[] = $this->get_permission( self::PERMISSION_EVENTS, 'admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ), sprintf( $fs->get_text_inline( 'View Basic %s Info', 'permissions-events' ), $fs->get_module_label() ), sprintf( /* translators: %s: 'Plugin' or 'Theme' */ $fs->get_text_inline( 'Current %s & SDK versions, and if active or uninstalled', 'permissions-events_desc' ), $fs->get_module_label( true ) ), '', 20, $is_optional, true, $load_default_from_storage ); return $permissions; } #endregion #-------------------------------------------------------------------------------- #region License Activation Permissions #-------------------------------------------------------------------------------- /** * @param array[] $extra_permissions * * @return array[] */ function get_license_activation_permissions( array $extra_permissions = array(), $include_optional_label = true ) { $permissions = array_merge( $this->get_license_required_permissions(), $this->get_license_optional_permissions( $include_optional_label ), $extra_permissions ); return $this->get_sorted_permissions_by_priority( $permissions ); } /** * @param bool $load_default_from_storage * * @return array[] */ function get_license_required_permissions( $load_default_from_storage = false ) { // Alias. $fs = $this->_fs; $permissions = array(); $permissions[] = $this->get_permission( self::PERMISSION_ESSENTIALS, 'admin-links', $fs->get_text_inline( 'View License Essentials', 'permissions-essentials' ), $fs->get_text_inline( sprintf( /* translators: %s: 'Plugin' or 'Theme' */ 'Homepage URL, %s version, SDK version', $fs->get_module_label() ), 'permissions-essentials_desc' ), sprintf( /* translators: %s: 'Plugin' or 'Theme' */ $fs->get_text_inline( 'To let you manage & control where the license is activated and ensure %s security & feature updates are only delivered to websites you authorize.', 'permissions-essentials_tooltip' ), $fs->get_module_label( true ) ), 10, false, true, $load_default_from_storage ); $permissions[] = $this->get_permission( self::PERMISSION_EVENTS, 'admin-' . ( $fs->is_plugin() ? 'plugins' : 'appearance' ), sprintf( $fs->get_text_inline( 'View %s State', 'permissions-events' ), $fs->get_module_label() ), sprintf( /* translators: %s: 'Plugin' or 'Theme' */ $fs->get_text_inline( 'Is active, deactivated, or uninstalled', 'permissions-events_desc-paid' ), $fs->get_module_label( true ) ), sprintf( $fs->get_text_inline( 'So you can reuse the license when the %s is no longer active.', 'permissions-events_tooltip' ), $fs->get_module_label( true ) ), 20, false, true, $load_default_from_storage ); return $permissions; } /** * @return array[] */ function get_license_optional_permissions( $include_optional_label = false, $load_default_from_storage = false ) { return array( $this->get_diagnostic_permission( $include_optional_label, $load_default_from_storage ), $this->get_extensions_permission( true, $include_optional_label, $load_default_from_storage ), ); } /** * @param bool $include_optional_label * @param bool $load_default_from_storage * * @return array */ function get_diagnostic_permission( $include_optional_label = false, $load_default_from_storage = false ) { return $this->get_permission( self::PERMISSION_DIAGNOSTIC, 'wordpress-alt', $this->_fs->get_text_inline( 'View Diagnostic Info', 'permissions-diagnostic' ) . ( $include_optional_label ? ' (' . $this->_fs->get_text_inline( 'optional' ) . ')' : '' ), $this->_fs->get_text_inline( 'WordPress & PHP versions, site language & title', 'permissions-diagnostic_desc' ), sprintf( /* translators: %s: 'Plugin' or 'Theme' */ $this->_fs->get_text_inline( 'To avoid breaking your website due to WordPress or PHP version incompatibilities, and recognize which languages & regions the %s should be translated and tailored to.', 'permissions-diagnostic_tooltip' ), $this->_fs->get_module_label( true ) ), 25, true, true, $load_default_from_storage ); } #endregion #-------------------------------------------------------------------------------- #region Common Permissions #-------------------------------------------------------------------------------- /** * @param bool $is_license_activation * @param bool $include_optional_label * @param bool $load_default_from_storage * * @return array */ function get_extensions_permission( $is_license_activation, $include_optional_label = false, $load_default_from_storage = false ) { $is_on_by_default = ! $is_license_activation; return $this->get_permission( self::PERMISSION_EXTENSIONS, 'block-default', $this->_fs->get_text_inline( 'View Plugins & Themes List', 'permissions-extensions' ) . ( $is_license_activation ? ( $include_optional_label ? ' (' . $this->_fs->get_text_inline( 'optional' ) . ')' : '' ) : '' ), $this->_fs->get_text_inline( 'Names, slugs, versions, and if active or not', 'permissions-extensions_desc' ), $this->_fs->get_text_inline( 'To ensure compatibility and avoid conflicts with your installed plugins and themes.', 'permissions-events_tooltip' ), 25, true, $is_on_by_default, $load_default_from_storage ); } /** * @param bool $load_default_from_storage * * @return array */ function get_user_permission( $load_default_from_storage = false ) { return $this->get_permission( self::PERMISSION_USER, 'admin-users', $this->_fs->get_text_inline( 'View Basic Profile Info', 'permissions-profile' ), $this->_fs->get_text_inline( 'Your WordPress user\'s: first & last name, and email address', 'permissions-profile_desc' ), $this->_fs->get_text_inline( 'Never miss important updates, get security warnings before they become public knowledge, and receive notifications about special offers and awesome new features.', 'permissions-profile_tooltip' ), 5, false, true, $load_default_from_storage ); } #endregion #-------------------------------------------------------------------------------- #region Optional Permissions #-------------------------------------------------------------------------------- /** * @return array[] */ function get_newsletter_permission() { return $this->get_permission( self::PERMISSION_NEWSLETTER, 'email-alt', $this->_fs->get_text_inline( 'Newsletter', 'permissions-newsletter' ), $this->_fs->get_text_inline( 'Updates, announcements, marketing, no spam', 'permissions-newsletter_desc' ), '', 15 ); } #endregion #-------------------------------------------------------------------------------- #region Permissions Storage #-------------------------------------------------------------------------------- /** * @param int|null $blog_id * * @return bool */ function is_extensions_tracking_allowed( $blog_id = null ) { return $this->is_permission_allowed( self::PERMISSION_EXTENSIONS, ! $this->_fs->is_premium(), $blog_id ); } /** * @param int|null $blog_id * * @return bool */ function is_essentials_tracking_allowed( $blog_id = null ) { return $this->is_permission_allowed( self::PERMISSION_ESSENTIALS, true, $blog_id ); } /** * @param bool $default * * @return bool */ function is_diagnostic_tracking_allowed( $default = true ) { return $this->is_premium_context() ? $this->is_permission_allowed( self::PERMISSION_DIAGNOSTIC, $default ) : $this->is_permission_allowed( self::PERMISSION_SITE, $default ); } /** * @param int|null $blog_id * * @return bool */ function is_homepage_url_tracking_allowed( $blog_id = null ) { return $this->is_permission_allowed( $this->get_site_permission_name(), true, $blog_id ); } /** * @param int|null $blog_id * * @return bool */ function update_site_tracking( $is_enabled, $blog_id = null, $only_if_not_set = false ) { $permissions = $this->get_site_tracking_permission_names(); $result = true; foreach ( $permissions as $permission ) { if ( ! $only_if_not_set || ! $this->is_permission_set( $permission, $blog_id ) ) { $result = ( $result && $this->update_permission_tracking_flag( $permission, $is_enabled, $blog_id ) ); } } return $result; } /** * @param string $permission * @param bool $default * @param int|null $blog_id * * @return bool */ function is_permission_allowed( $permission, $default = false, $blog_id = null ) { if ( ! self::is_supported_permission( $permission ) ) { return $default; } return $this->is_permission( $permission, true, $blog_id ); } /** * @param string $permission * @param bool $is_allowed * @param int|null $blog_id * * @return bool */ function is_permission( $permission, $is_allowed, $blog_id = null ) { if ( ! self::is_supported_permission( $permission ) ) { return false; } $tag = "is_{$permission}_tracking_allowed"; return ( $is_allowed === $this->_fs->apply_filters( $tag, $this->_storage->get( $tag, $this->get_permission_default( $permission ), $blog_id, FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED ) ) ); } /** * @param string $permission * @param int|null $blog_id * * @return bool */ function is_permission_set( $permission, $blog_id = null ) { $tag = "is_{$permission}_tracking_allowed"; $permission = $this->_storage->get( $tag, null, $blog_id, FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED ); return is_bool( $permission ); } /** * @param string[] $permissions * @param bool $is_allowed * * @return bool `true` if all given permissions are in sync with `$is_allowed`. */ function are_permissions( $permissions, $is_allowed, $blog_id = null ) { foreach ( $permissions as $permission ) { if ( ! $this->is_permission( $permission, $is_allowed, $blog_id ) ) { return false; } } return true; } /** * @param string $permission * @param bool $is_enabled * @param int|null $blog_id * * @return bool `false` if permission not supported or `$is_enabled` is not a boolean. */ function update_permission_tracking_flag( $permission, $is_enabled, $blog_id = null ) { if ( is_bool( $is_enabled ) && self::is_supported_permission( $permission ) ) { $this->_storage->store( "is_{$permission}_tracking_allowed", $is_enabled, $blog_id, FS_Storage::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED ); return true; } return false; } /** * @param array $permissions */ function update_permissions_tracking_flag( $permissions ) { foreach ( $permissions as $permission => $is_enabled ) { $this->update_permission_tracking_flag( $permission, $is_enabled ); } } #endregion /** * @param string $permission * * @return bool */ function get_permission_default( $permission ) { if ( $this->_fs->is_premium() && self::PERMISSION_EXTENSIONS === $permission ) { return false; } // All permissions except for the extensions in paid version are on by default when the user opts in to usage tracking. return true; } /** * @return string */ function get_site_permission_name() { return $this->is_premium_context() ? self::PERMISSION_ESSENTIALS : self::PERMISSION_SITE; } /** * @return string[] */ function get_site_tracking_permission_names() { return $this->is_premium_context() ? array( FS_Permission_Manager::PERMISSION_ESSENTIALS, FS_Permission_Manager::PERMISSION_EVENTS, ) : array( FS_Permission_Manager::PERMISSION_SITE ); } #-------------------------------------------------------------------------------- #region Rendering #-------------------------------------------------------------------------------- /** * @param array $permission */ function render_permission( array $permission ) { fs_require_template( 'connect/permission.php', $permission ); } /** * @param array $permissions_group */ function render_permissions_group( array $permissions_group ) { $permissions_group[ 'fs' ] = $this->_fs; fs_require_template( 'connect/permissions-group.php', $permissions_group ); } function require_permissions_js() { fs_require_once_template( 'js/permissions.php', $params ); } #endregion #-------------------------------------------------------------------------------- #region Helper Methods #-------------------------------------------------------------------------------- /** * @param string $id * @param string $dashicon * @param string $label * @param string $desc * @param string $tooltip * @param int $priority * @param bool $is_optional * @param bool $is_on_by_default * @param bool $load_from_storage * * @return array */ private function get_permission( $id, $dashicon, $label, $desc, $tooltip = '', $priority = 10, $is_optional = false, $is_on_by_default = true, $load_from_storage = false ) { $is_on = $load_from_storage ? $this->is_permission_allowed( $id, $is_on_by_default ) : $is_on_by_default; return array( 'id' => $id, 'icon-class' => $this->_fs->apply_filters( "permission_{$id}_icon", "dashicons dashicons-{$dashicon}" ), 'label' => $this->_fs->apply_filters( "permission_{$id}_label", $label ), 'tooltip' => $this->_fs->apply_filters( "permission_{$id}_tooltip", $tooltip ), 'desc' => $this->_fs->apply_filters( "permission_{$id}_desc", $desc ), 'priority' => $this->_fs->apply_filters( "permission_{$id}_priority", $priority ), 'optional' => $is_optional, 'default' => $this->_fs->apply_filters( "permission_{$id}_default", $is_on ), ); } /** * @param array $permissions * * @return array[] */ private function get_sorted_permissions_by_priority( array $permissions ) { // Allow filtering of the permissions list. $permissions = $this->_fs->apply_filters( 'permission_list', $permissions ); // Sort by priority. uasort( $permissions, 'fs_sort_by_priority' ); return $permissions; } #endregion }freemius/includes/managers/class-fs-option-manager.php000064400000034211147600046700017135 0ustar00_logger = FS_Logger::get_logger( WP_FS__SLUG . '_opt_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); $this->_logger->entrance(); $this->_logger->log( 'id = ' . $id ); $this->_id = $id; $this->_autoload = $autoload; if ( is_multisite() ) { $this->_is_network_storage = ( true === $network_level_or_blog_id ); if ( is_numeric( $network_level_or_blog_id ) ) { $this->_blog_id = $network_level_or_blog_id; } } else { $this->_is_network_storage = false; } if ( $load ) { $this->load(); } } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @param string $id * @param bool $load * @param bool|int $network_level_or_blog_id Since 2.0.0 * @param bool|null $autoload * * @return \FS_Option_Manager */ static function get_manager( $id, $load = false, $network_level_or_blog_id = false, $autoload = null ) { $key = strtolower( $id ); if ( is_multisite() ) { if ( true === $network_level_or_blog_id ) { $key .= ':ms'; } else if ( is_numeric( $network_level_or_blog_id ) && $network_level_or_blog_id > 0 ) { $key .= ":{$network_level_or_blog_id}"; } else { $network_level_or_blog_id = get_current_blog_id(); $key .= ":{$network_level_or_blog_id}"; } } if ( ! isset( self::$_MANAGERS[ $key ] ) ) { self::$_MANAGERS[ $key ] = new FS_Option_Manager( $id, $load, $network_level_or_blog_id, $autoload ); } // If load required but not yet loaded, load. else if ( $load && ! self::$_MANAGERS[ $key ]->is_loaded() ) { self::$_MANAGERS[ $key ]->load(); } return self::$_MANAGERS[ $key ]; } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @param bool $flush */ function load( $flush = false ) { $this->_logger->entrance(); if ( ! $flush && isset( $this->_options ) ) { return; } if ( isset( $this->_options ) ) { // Clear prev options. $this->clear(); } $option_name = $this->get_option_manager_name(); if ( $this->_is_network_storage ) { $this->_options = get_site_option( $option_name ); } else if ( $this->_blog_id > 0 ) { $this->_options = get_blog_option( $this->_blog_id, $option_name ); } else { $this->_options = get_option( $option_name ); } if ( is_string( $this->_options ) ) { $this->_options = json_decode( $this->_options ); } // $this->_logger->info('get_option = ' . var_export($this->_options, true)); if ( false === $this->_options ) { $this->clear(); } } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @return bool */ function is_loaded() { return isset( $this->_options ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @return bool */ function is_empty() { return ( $this->is_loaded() && false === $this->_options ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param bool $flush */ function clear( $flush = false ) { $this->_logger->entrance(); $this->_options = array(); if ( $flush ) { $this->store(); } } /** * Delete options manager from DB. * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ function delete() { $option_name = $this->get_option_manager_name(); if ( $this->_is_network_storage ) { delete_site_option( $option_name ); } else if ( $this->_blog_id > 0 ) { delete_blog_option( $this->_blog_id, $option_name ); } else { delete_option( $option_name ); } } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param string $option * @param bool $flush * * @return bool */ function has_option( $option, $flush = false ) { if ( ! $this->is_loaded() || $flush ) { $this->load( $flush ); } return array_key_exists( $option, $this->_options ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @param string $option * @param mixed $default * @param bool $flush * * @return mixed */ function get_option( $option, $default = null, $flush = false ) { $this->_logger->entrance( 'option = ' . $option ); if ( ! $this->is_loaded() || $flush ) { $this->load( $flush ); } if ( is_array( $this->_options ) ) { $value = isset( $this->_options[ $option ] ) ? $this->_options[ $option ] : $default; } else if ( is_object( $this->_options ) ) { $value = isset( $this->_options->{$option} ) ? $this->_options->{$option} : $default; } else { $value = $default; } /** * If it's an object, return a clone of the object, otherwise, * external changes of the object will actually change the value * of the object in the option manager which may lead to an unexpected * behaviour and data integrity when a store() call is triggered. * * Example: * $object1 = $options->get_option( 'object1' ); * $object1->x = 123; * * $object2 = $options->get_option( 'object2' ); * $object2->y = 'dummy'; * * $options->set_option( 'object2', $object2, true ); * * If we don't return a clone of option 'object1', setting 'object2' * will also store the updated value of 'object1' which is quite not * an expected behaviour. * * @author Vova Feldman */ return is_object( $value ) ? clone $value : $value; } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @param string $option * @param mixed $value * @param bool $flush */ function set_option( $option, $value, $flush = false ) { $this->_logger->entrance( 'option = ' . $option ); if ( ! $this->is_loaded() ) { $this->clear(); } /** * If it's an object, store a clone of the object, otherwise, * external changes of the object will actually change the value * of the object in the options manager which may lead to an unexpected * behaviour and data integrity when a store() call is triggered. * * Example: * $object1 = new stdClass(); * $object1->x = 123; * * $options->set_option( 'object1', $object1 ); * * $object1->x = 456; * * $options->set_option( 'object2', $object2, true ); * * If we don't set the option as a clone of option 'object1', setting 'object2' * will also store the updated value of 'object1' ($object1->x = 456 instead of * $object1->x = 123) which is quite not an expected behaviour. * * @author Vova Feldman */ $copy = is_object( $value ) ? clone $value : $value; if ( is_array( $this->_options ) ) { $this->_options[ $option ] = $copy; } else if ( is_object( $this->_options ) ) { $this->_options->{$option} = $copy; } if ( $flush ) { $this->store(); } } /** * Unset option. * * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @param string $option * @param bool $flush */ function unset_option( $option, $flush = false ) { $this->_logger->entrance( 'option = ' . $option ); if ( is_array( $this->_options ) ) { if ( ! isset( $this->_options[ $option ] ) ) { return; } unset( $this->_options[ $option ] ); } else if ( is_object( $this->_options ) ) { if ( ! isset( $this->_options->{$option} ) ) { return; } unset( $this->_options->{$option} ); } if ( $flush ) { $this->store(); } } /** * Dump options to database. * * @author Vova Feldman (@svovaf) * @since 1.0.3 */ function store() { $this->_logger->entrance(); $option_name = $this->get_option_manager_name(); if ( $this->_logger->is_on() ) { $this->_logger->info( $option_name . ' = ' . var_export( $this->_options, true ) ); } // Update DB. if ( $this->_is_network_storage ) { update_site_option( $option_name, $this->_options ); } else if ( $this->_blog_id > 0 ) { update_blog_option( $this->_blog_id, $option_name, $this->_options ); } else { update_option( $option_name, $this->_options, $this->_autoload ); } } /** * Get options keys. * * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @return string[] */ function get_options_keys() { if ( is_array( $this->_options ) ) { return array_keys( $this->_options ); } else if ( is_object( $this->_options ) ) { return array_keys( get_object_vars( $this->_options ) ); } return array(); } #-------------------------------------------------------------------------------- #region Migration #-------------------------------------------------------------------------------- /** * Migrate options from site level. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function migrate_to_network() { $site_options = FS_Option_Manager::get_manager($this->_id, true, false); $options = is_object( $site_options->_options ) ? get_object_vars( $site_options->_options ) : $site_options->_options; if ( ! empty( $options ) ) { foreach ( $options as $key => $val ) { $this->set_option( $key, $val, false ); } $this->store(); } } #endregion #-------------------------------------------------------------------------------- #region Helper Methods #-------------------------------------------------------------------------------- /** * @return string */ private function get_option_manager_name() { return $this->_id; } #endregion } freemius/includes/managers/class-fs-license-manager.php000064400000004226147600046700017252 0ustar00get_slug() ); // // if ( ! isset( self::$_instances[ $slug ] ) ) { // self::$_instances[ $slug ] = new FS_License_Manager( $slug, $fs ); // } // // return self::$_instances[ $slug ]; // } // //// private function __construct($slug) { //// parent::__construct($slug); //// } // // function entry_id() { // return 'licenses'; // } // // function sync( $id ) { // // } // // /** // * @author Vova Feldman (@svovaf) // * @since 1.0.5 // * @uses FS_Api // * // * @param number|bool $plugin_id // * // * @return FS_Plugin_License[]|stdClass Licenses or API error. // */ // function api_get_user_plugin_licenses( $plugin_id = false ) { // $api = $this->_fs->get_api_user_scope(); // // if ( ! is_numeric( $plugin_id ) ) { // $plugin_id = $this->_fs->get_id(); // } // // $result = $api->call( "/plugins/{$plugin_id}/licenses.json" ); // // if ( ! isset( $result->error ) ) { // for ( $i = 0, $len = count( $result->licenses ); $i < $len; $i ++ ) { // $result->licenses[ $i ] = new FS_Plugin_License( $result->licenses[ $i ] ); // } // // $result = $result->licenses; // } // // return $result; // } // // function api_get_many() { // // } // // function api_activate( $id ) { // // } // // function api_deactivate( $id ) { // // } /** * @param FS_Plugin_License[] $licenses * * @return bool */ static function has_premium_license( $licenses ) { if ( is_array( $licenses ) ) { foreach ( $licenses as $license ) { /** * @var FS_Plugin_License $license */ if ( ! $license->is_utilized() && $license->is_features_enabled() ) { return true; } } } return false; } }freemius/includes/managers/class-fs-key-value-storage.php000064400000024374147600046700017572 0ustar00 0 ) { $key .= ":{$network_level_or_blog_id}"; } else { $network_level_or_blog_id = get_current_blog_id(); $key .= ":{$network_level_or_blog_id}"; } } if ( ! isset( self::$_instances[ $key ] ) ) { self::$_instances[ $key ] = new FS_Key_Value_Storage( $id, $secondary_id, $network_level_or_blog_id ); } return self::$_instances[ $key ]; } protected function __construct( $id, $secondary_id, $network_level_or_blog_id = false ) { $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $secondary_id . '_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); $this->_id = $id; $this->_secondary_id = $secondary_id; if ( is_multisite() ) { $this->_is_multisite_storage = ( true === $network_level_or_blog_id ); if ( is_numeric( $network_level_or_blog_id ) ) { $this->_blog_id = $network_level_or_blog_id; } } else { $this->_is_multisite_storage = false; } $this->load(); } protected function get_option_manager() { return FS_Option_Manager::get_manager( WP_FS__ACCOUNTS_OPTION_NAME, true, $this->_is_multisite_storage ? true : ( $this->_blog_id > 0 ? $this->_blog_id : false ) ); } protected function get_all_data() { return $this->get_option_manager()->get_option( $this->_id, array() ); } /** * Load plugin data from local DB. * * @author Vova Feldman (@svovaf) * @since 1.0.7 */ function load() { $all_plugins_data = $this->get_all_data(); $this->_data = isset( $all_plugins_data[ $this->_secondary_id ] ) ? $all_plugins_data[ $this->_secondary_id ] : array(); } /** * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param string $key * @param mixed $value * @param bool $flush */ function store( $key, $value, $flush = true ) { if ( $this->_logger->is_on() ) { $this->_logger->entrance( $key . ' = ' . var_export( $value, true ) ); } if ( array_key_exists( $key, $this->_data ) && $value === $this->_data[ $key ] ) { // No need to store data if the value wasn't changed. return; } $all_data = $this->get_all_data(); $this->_data[ $key ] = $value; $all_data[ $this->_secondary_id ] = $this->_data; $options_manager = $this->get_option_manager(); $options_manager->set_option( $this->_id, $all_data, $flush ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function save() { $this->get_option_manager()->store(); } /** * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param bool $store * @param string[] $exceptions Set of keys to keep and not clear. */ function clear_all( $store = true, $exceptions = array() ) { $new_data = array(); foreach ( $exceptions as $key ) { if ( isset( $this->_data[ $key ] ) ) { $new_data[ $key ] = $this->_data[ $key ]; } } $this->_data = $new_data; if ( $store ) { $all_data = $this->get_all_data(); $all_data[ $this->_secondary_id ] = $this->_data; $options_manager = $this->get_option_manager(); $options_manager->set_option( $this->_id, $all_data, true ); } } /** * Delete key-value storage. * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ function delete() { $this->_data = array(); $all_data = $this->get_all_data(); unset( $all_data[ $this->_secondary_id ] ); $options_manager = $this->get_option_manager(); $options_manager->set_option( $this->_id, $all_data, true ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param string $key * @param bool $store */ function remove( $key, $store = true ) { if ( ! array_key_exists( $key, $this->_data ) ) { return; } unset( $this->_data[ $key ] ); if ( $store ) { $all_data = $this->get_all_data(); $all_data[ $this->_secondary_id ] = $this->_data; $options_manager = $this->get_option_manager(); $options_manager->set_option( $this->_id, $all_data, true ); } } /** * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param string $key * @param mixed $default * * @return bool|\FS_Plugin */ function get( $key, $default = false ) { return array_key_exists( $key, $this->_data ) ? $this->_data[ $key ] : $default; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return string */ function get_secondary_id() { return $this->_secondary_id; } /* ArrayAccess + Magic Access (better for refactoring) -----------------------------------------------------------------------------------*/ function __set( $k, $v ) { $this->store( $k, $v ); } function __isset( $k ) { return array_key_exists( $k, $this->_data ); } function __unset( $k ) { $this->remove( $k ); } function __get( $k ) { return $this->get( $k, null ); } #[ReturnTypeWillChange] function offsetSet( $k, $v ) { if ( is_null( $k ) ) { throw new Exception( 'Can\'t append value to request params.' ); } else { $this->{$k} = $v; } } #[ReturnTypeWillChange] function offsetExists( $k ) { return array_key_exists( $k, $this->_data ); } #[ReturnTypeWillChange] function offsetUnset( $k ) { unset( $this->$k ); } #[ReturnTypeWillChange] function offsetGet( $k ) { return $this->get( $k, null ); } /** * (PHP 5 >= 5.0.0)
    * Return the current element * * @link http://php.net/manual/en/iterator.current.php * @return mixed Can return any type. */ #[ReturnTypeWillChange] public function current() { return current( $this->_data ); } /** * (PHP 5 >= 5.0.0)
    * Move forward to next element * * @link http://php.net/manual/en/iterator.next.php * @return void Any returned value is ignored. */ #[ReturnTypeWillChange] public function next() { next( $this->_data ); } /** * (PHP 5 >= 5.0.0)
    * Return the key of the current element * * @link http://php.net/manual/en/iterator.key.php * @return mixed scalar on success, or null on failure. */ #[ReturnTypeWillChange] public function key() { return key( $this->_data ); } /** * (PHP 5 >= 5.0.0)
    * Checks if current position is valid * * @link http://php.net/manual/en/iterator.valid.php * @return boolean The return value will be casted to boolean and then evaluated. * Returns true on success or false on failure. */ #[ReturnTypeWillChange] public function valid() { $key = key( $this->_data ); return ( $key !== null && $key !== false ); } /** * (PHP 5 >= 5.0.0)
    * Rewind the Iterator to the first element * * @link http://php.net/manual/en/iterator.rewind.php * @return void Any returned value is ignored. */ #[ReturnTypeWillChange] public function rewind() { reset( $this->_data ); } /** * (PHP 5 >= 5.1.0)
    * Count elements of an object * * @link http://php.net/manual/en/countable.count.php * @return int The custom count as an integer. *

    *

    * The return value is cast to an integer. */ #[ReturnTypeWillChange] public function count() { return count( $this->_data ); } }freemius/includes/managers/class-fs-gdpr-manager.php000064400000013145147600046700016564 0ustar00_storage = FS_Option_Manager::get_manager( WP_FS__GDPR_OPTION_NAME, true, true ); $this->_wp_user_id = Freemius::get_current_wp_user_id(); $this->_option_name = "u{$this->_wp_user_id}"; $this->_data = $this->_storage->get_option( $this->_option_name, array() ); $this->_notices = FS_Admin_Notices::instance( 'all_admins', '', '', true ); if ( ! is_array( $this->_data ) ) { $this->_data = array(); } } /** * Update a GDPR option for the current admin and store it. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @param string $name * @param mixed $value */ private function update_option( $name, $value ) { $this->_data[ $name ] = $value; $this->_storage->set_option( $this->_option_name, $this->_data, true ); } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 * * @param bool $is_required */ public function store_is_required( $is_required ) { $this->update_option( 'required', $is_required ); } /** * Checks if the GDPR opt-in sticky notice is currently shown. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @return bool */ public function is_opt_in_notice_shown() { return $this->_notices->has_sticky( "gdpr_optin_actions_{$this->_wp_user_id}", true ); } /** * Remove the GDPR opt-in sticky notice. * * @author Vova Feldman (@svovaf) * @since 2.1.0 */ public function remove_opt_in_notice() { $this->_notices->remove_sticky( "gdpr_optin_actions_{$this->_wp_user_id}", true ); $this->disable_opt_in_notice(); } /** * Prevents the opt-in message from being added/shown. * * @author Leo Fajardo (@leorw) * @since 2.1.0 */ public function disable_opt_in_notice() { $this->update_option( 'show_opt_in_notice', false ); } /** * Checks if a GDPR opt-in message needs to be shown to the current admin. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @return bool */ public function should_show_opt_in_notice() { return ( ! isset( $this->_data['show_opt_in_notice'] ) || true === $this->_data['show_opt_in_notice'] ); } /** * Get the last time the GDPR opt-in notice was shown. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @return false|int */ public function last_time_notice_was_shown() { return isset( $this->_data['notice_shown_at'] ) ? $this->_data['notice_shown_at'] : false; } /** * Update the timestamp of the last time the GDPR opt-in message was shown to now. * * @author Vova Feldman (@svovaf) * @since 2.1.0 */ public function notice_was_just_shown() { $this->update_option( 'notice_shown_at', WP_FS__SCRIPT_START_TIME ); } /** * @param string $message * @param string|null $plugin_title * * @author Vova Feldman (@svovaf) * @since 2.1.0 */ public function add_opt_in_sticky_notice( $message, $plugin_title = null ) { $this->_notices->add_sticky( $message, "gdpr_optin_actions_{$this->_wp_user_id}", '', 'promotion', true, $this->_wp_user_id, $plugin_title, true ); } }freemius/includes/managers/class-fs-debug-manager.php000064400000041416147600046700016720 0ustar00entrance(); $title = sprintf( '%s [v.%s]', fs_text_inline( 'Freemius Debug' ), WP_FS__SDK_VERSION ); if ( WP_FS__DEV_MODE ) { // Add top-level debug menu item. $hook = FS_Admin_Menu_Manager::add_page( $title, $title, 'manage_options', 'freemius', array( self::class, '_debug_page_render' ) ); } else { // Add hidden debug page. $hook = FS_Admin_Menu_Manager::add_subpage( '', $title, $title, 'manage_options', 'freemius', array( self::class, '_debug_page_render' ) ); } if ( ! empty( $hook ) ) { add_action( "load-$hook", array( self::class, '_debug_page_actions' ) ); } } /** * @author Vova Feldman (@svovaf) * Moved from Freemius * * @since 1.0.8 */ static function _debug_page_actions() { Freemius::_clean_admin_content_section(); if ( fs_request_is_action( 'restart_freemius' ) ) { check_admin_referer( 'restart_freemius' ); if ( ! is_multisite() ) { // Clear accounts data. Freemius::get_accounts()->clear( null, true ); } else { $sites = Freemius::get_sites(); foreach ( $sites as $site ) { $blog_id = Freemius::get_site_blog_id( $site ); Freemius::get_accounts()->clear( $blog_id, true ); } // Clear network level storage. Freemius::get_accounts()->clear( true, true ); } // Clear SDK reference cache. delete_option( 'fs_active_plugins' ); } else if ( fs_request_is_action( 'clear_updates_data' ) ) { check_admin_referer( 'clear_updates_data' ); if ( ! is_multisite() ) { set_site_transient( 'update_plugins', null ); set_site_transient( 'update_themes', null ); } else { $current_blog_id = get_current_blog_id(); $sites = Freemius::get_sites(); foreach ( $sites as $site ) { switch_to_blog( Freemius::get_site_blog_id( $site ) ); set_site_transient( 'update_plugins', null ); set_site_transient( 'update_themes', null ); } switch_to_blog( $current_blog_id ); } } else if ( fs_request_is_action( 'reset_deactivation_snoozing' ) ) { check_admin_referer( 'reset_deactivation_snoozing' ); Freemius::reset_deactivation_snoozing(); } else if ( fs_request_is_action( 'simulate_trial' ) ) { check_admin_referer( 'simulate_trial' ); $fs = freemius( fs_request_get( 'module_id' ) ); // Update SDK install to at least 24 hours before. $fs->get_storage()->install_timestamp = ( time() - WP_FS__TIME_24_HOURS_IN_SEC ); // Unset the trial shown timestamp. unset( $fs->get_storage()->trial_promotion_shown ); } else if ( fs_request_is_action( 'simulate_network_upgrade' ) ) { check_admin_referer( 'simulate_network_upgrade' ); $fs = freemius( fs_request_get( 'module_id' ) ); Freemius::set_network_upgrade_mode( $fs->get_storage() ); } else if ( fs_request_is_action( 'delete_install' ) ) { check_admin_referer( 'delete_install' ); Freemius::_delete_site_by_slug( fs_request_get( 'slug' ), fs_request_get( 'module_type' ), true, fs_request_get( 'blog_id', null ) ); } else if ( fs_request_is_action( 'delete_user' ) ) { check_admin_referer( 'delete_user' ); self::delete_user( fs_request_get( 'user_id' ) ); } else if ( fs_request_is_action( 'download_logs' ) ) { check_admin_referer( 'download_logs' ); $download_url = FS_Logger::download_db_logs( fs_request_get( 'filters', false, 'post' ) ); if ( false === $download_url ) { wp_die( 'Oops... there was an error while generating the logs download file. Please try again and if it doesn\'t work contact support@freemius.com.' ); } fs_redirect( $download_url ); } else if ( fs_request_is_action( 'migrate_options_to_network' ) ) { check_admin_referer( 'migrate_options_to_network' ); Freemius::migrate_options_to_network(); } } /** * @author Vova Feldman (@svovaf) * Moved from Freemius * * @since 1.0.8 */ static function _debug_page_render() { Freemius::get_static_logger()->entrance(); $all_modules_sites = self::get_all_modules_sites(); $licenses_by_module_type = self::get_all_licenses_by_module_type(); $vars = array( 'plugin_sites' => $all_modules_sites[ WP_FS__MODULE_TYPE_PLUGIN ], 'theme_sites' => $all_modules_sites[ WP_FS__MODULE_TYPE_THEME ], 'users' => Freemius::get_all_users(), 'addons' => Freemius::get_all_addons(), 'account_addons' => Freemius::get_all_account_addons(), 'plugin_licenses' => $licenses_by_module_type[ WP_FS__MODULE_TYPE_PLUGIN ], 'theme_licenses' => $licenses_by_module_type[ WP_FS__MODULE_TYPE_THEME ], ); fs_enqueue_local_style( 'fs_debug', '/admin/debug.css' ); fs_require_once_template( 'debug.php', $vars ); } /** * @author Vova Feldman (@svovaf) * Moved from Freemius * * @since 1.2.1.6 */ static function _get_debug_log() { check_admin_referer( 'fs_get_debug_log' ); if ( ! is_super_admin() ) { return; } if (!FS_Logger::is_storage_logging_on()) { return; } $limit = min( ! empty( $_POST['limit'] ) ? absint( $_POST['limit'] ) : 200, 200 ); $offset = min( ! empty( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 200, 200 ); $logs = FS_Logger::load_db_logs( fs_request_get( 'filters', false, 'post' ), $limit, $offset ); Freemius::shoot_ajax_success( $logs ); } /** * @author Vova Feldman (@svovaf) * Moved from Freemius * * @since 1.2.1.7 */ static function _get_db_option() { check_admin_referer( 'fs_get_db_option' ); $option_name = fs_request_get( 'option_name' ); if ( ! is_super_admin() || ! fs_starts_with( $option_name, 'fs_' ) ) { Freemius::shoot_ajax_failure(); } $value = get_option( $option_name ); $result = array( 'name' => $option_name, ); if ( false !== $value ) { if ( ! is_string( $value ) ) { $value = json_encode( $value ); } $result['value'] = $value; } Freemius::shoot_ajax_success( $result ); } /** * @author Vova Feldman (@svovaf) * Moved from Freemius * * @since 1.2.1.7 */ static function _set_db_option() { check_admin_referer( 'fs_set_db_option' ); $option_name = fs_request_get( 'option_name' ); if ( ! is_super_admin() || ! fs_starts_with( $option_name, 'fs_' ) ) { Freemius::shoot_ajax_failure(); } $option_value = fs_request_get_raw( 'option_value' ); if ( ! empty( $option_value ) ) { update_option( $option_name, $option_value ); } Freemius::shoot_ajax_success(); } /** * @author Vova Feldman (@svovaf) * Moved from Freemius * * @since 1.1.7.3 */ static function _toggle_debug_mode() { check_admin_referer( 'fs_toggle_debug_mode' ); if ( ! is_super_admin() ) { return; } $is_on = fs_request_get( 'is_on', false, 'post' ); if ( fs_request_is_post() && in_array( $is_on, array( 0, 1 ) ) ) { if ( $is_on ) { self::_turn_on_debug_mode(); } else { self::_turn_off_debug_mode(); } // Logic to turn debugging off automatically if ( 1 == $is_on ) { // Plan a single event triggering after 24 hours to turn debugging off. wp_schedule_single_event( time() + 24 * HOUR_IN_SECONDS, 'fs_debug_turn_off_logging_hook' ); } else { // Cancels any planned event when debugging is turned off manually. $timestamp = wp_next_scheduled( 'fs_debug_turn_off_logging_hook' ); if ( $timestamp ) { wp_unschedule_event( $timestamp, 'fs_debug_turn_off_logging_hook' ); } } } exit; } /** * @author Daniele Alessandra (@danielealessandra) * @since 2.6.2 * */ static function _turn_off_debug_mode() { self::update_debug_mode_option( 0 ); FS_Logger::_set_storage_logging( false ); } /** * @author Daniele Alessandra (@danielealessandra) * @since 2.6.2 * */ static function _turn_on_debug_mode() { self::update_debug_mode_option( 1 ); FS_Logger::_set_storage_logging(); } /** * @author Leo Fajardo (@leorw) * Moved from Freemius * * @param string $url * @param array $request * * @since 2.1.0 * */ public static function enrich_request_for_debug( &$url, &$request ) { if ( WP_FS__DEBUG_SDK || isset( $_COOKIE['XDEBUG_SESSION'] ) ) { $url = add_query_arg( 'XDEBUG_SESSION_START', rand( 0, 9999999 ), $url ); $url = add_query_arg( 'XDEBUG_SESSION', 'PHPSTORM', $url ); $request['cookies'] = array( new WP_Http_Cookie( array( 'name' => 'XDEBUG_SESSION', 'value' => 'PHPSTORM', ) ), ); } } /** * @author Leo Fajardo (@leorw) * Moved from Freemius * * @return array * * @since 2.0.0 * */ private static function get_all_licenses_by_module_type() { $licenses = Freemius::get_account_option( 'all_licenses' ); $licenses_by_module_type = array( WP_FS__MODULE_TYPE_PLUGIN => array(), WP_FS__MODULE_TYPE_THEME => array(), ); if ( ! is_array( $licenses ) ) { return $licenses_by_module_type; } foreach ( $licenses as $module_id => $module_licenses ) { $fs = Freemius::get_instance_by_id( $module_id ); if ( false === $fs ) { continue; } $licenses_by_module_type[ $fs->get_module_type() ] = array_merge( $licenses_by_module_type[ $fs->get_module_type() ], $module_licenses ); } return $licenses_by_module_type; } /** * Moved from the Freemius class. * * @author Leo Fajardo (@leorw) * * @return array * * @since 2.5.0 */ static function get_all_modules_sites() { Freemius::get_static_logger()->entrance(); $sites_by_type = array( WP_FS__MODULE_TYPE_PLUGIN => array(), WP_FS__MODULE_TYPE_THEME => array(), ); $module_types = array_keys( $sites_by_type ); if ( ! is_multisite() ) { foreach ( $module_types as $type ) { $sites_by_type[ $type ] = Freemius::get_all_sites( $type ); foreach ( $sites_by_type[ $type ] as $slug => $install ) { $sites_by_type[ $type ][ $slug ] = array( $install ); } } } else { $sites = Freemius::get_sites(); foreach ( $sites as $site ) { $blog_id = Freemius::get_site_blog_id( $site ); foreach ( $module_types as $type ) { $installs = Freemius::get_all_sites( $type, $blog_id ); foreach ( $installs as $slug => $install ) { if ( ! isset( $sites_by_type[ $type ][ $slug ] ) ) { $sites_by_type[ $type ][ $slug ] = array(); } $install->blog_id = $blog_id; $sites_by_type[ $type ][ $slug ][] = $install; } } } } return $sites_by_type; } /** * Delete user. * * @author Vova Feldman (@svovaf) * * @param number $user_id * @param bool $store * * @return false|int The user ID if deleted. Otherwise, FALSE (when install not exist). * @since 2.0.0 * */ public static function delete_user( $user_id, $store = true ) { $users = Freemius::get_all_users(); if ( ! is_array( $users ) || ! isset( $users[ $user_id ] ) ) { return false; } unset( $users[ $user_id ] ); self::$_accounts->set_option( 'users', $users, $store ); return $user_id; } /** * @author Daniele Alessandra (@danielealessandra) * * @return void * @since 2.6.2 * */ public static function load_required_static() { if ( ! WP_FS__DEMO_MODE ) { add_action( ( fs_is_network_admin() ? 'network_' : '' ) . 'admin_menu', array( self::class, '_add_debug_section', ) ); } add_action( "wp_ajax_fs_toggle_debug_mode", array( self::class, '_toggle_debug_mode' ) ); Freemius::add_ajax_action_static( 'get_debug_log', array( self::class, '_get_debug_log' ) ); Freemius::add_ajax_action_static( 'get_db_option', array( self::class, '_get_db_option' ) ); Freemius::add_ajax_action_static( 'set_db_option', array( self::class, '_set_db_option' ) ); } /** * @author Daniele Alessandra (@danielealessandra) * * @return void * * @since 2.6.2 */ public static function register_hooks() { add_action( 'fs_debug_turn_off_logging_hook', array( self::class, '_turn_off_debug_mode' ) ); } /** * @author Daniele Alessandra (@danielealessandra) * * @param int $is_on * * @return void * * @since 2.6.2 */ private static function update_debug_mode_option( $is_on ) { update_option( 'fs_debug_mode', $is_on ); } } freemius/includes/managers/class-fs-contact-form-manager.php000064400000004071147600046700020222 0ustar00 */ public function get_query_params( Freemius $fs ) { $context_params = array( 'plugin_id' => $fs->get_id(), 'plugin_public_key' => $fs->get_public_key(), 'plugin_version' => $fs->get_plugin_version(), ); // Get site context secure params. if ( $fs->is_registered() ) { $context_params = array_merge( $context_params, FS_Security::instance()->get_context_params( $fs->get_site(), time(), 'contact' ) ); } return array_merge( $_GET, array_merge( $context_params, array( 'plugin_version' => $fs->get_plugin_version(), 'wp_login_url' => wp_login_url(), 'site_url' => Freemius::get_unfiltered_site_url(), // 'wp_admin_css' => get_bloginfo('wpurl') . "/wp-admin/load-styles.php?c=1&load=buttons,wp-admin,dashicons", ) ) ); } /** * Retrieves the standalone link to the Freemius Contact Form. * * @param Freemius $fs * * @return string */ public function get_standalone_link( Freemius $fs ) { $query_params = $this->get_query_params( $fs ); $query_params['is_standalone'] = 'true'; $query_params['parent_url'] = admin_url( add_query_arg( null, null ) ); return WP_FS__ADDRESS . '/contact/?' . http_build_query( $query_params ); } }freemius/includes/managers/class-fs-clone-manager.php000064400000173431147600046700016735 0ustar00_storage = FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true ); $this->_network_storage = FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true, true ); $this->maybe_migrate_options(); $this->_notices = FS_Admin_Notices::instance( 'global_clone_resolution_notices', '', '', true ); $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . '_clone_manager', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); } /** * Migrate clone resolution options from 2.5.0 array-based structure, to a new flat structure. * * The reason this logic is not in a separate migration script is that we want to be 100% sure data is migrated before any execution of clone logic. * * @todo Delete this one in the future. */ private function maybe_migrate_options() { $storages = array( $this->_storage, $this->_network_storage ); foreach ( $storages as $storage ) { $clone_data = $storage->get_option( self::OPTION_NAME ); if ( is_array( $clone_data ) && ! empty( $clone_data ) ) { foreach ( $clone_data as $key => $val ) { if ( ! is_null( $val ) ) { $storage->set_option( $key, $val ); } } $storage->unset_option( self::OPTION_NAME, true ); } } } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function _init() { if ( is_admin() ) { if ( Freemius::is_admin_post() ) { add_action( 'admin_post_fs_clone_resolution', array( $this, '_handle_clone_resolution' ) ); } if ( Freemius::is_ajax() ) { Freemius::add_ajax_action_static( 'handle_clone_resolution', array( $this, '_clone_resolution_action_ajax_handler' ) ); } else { if ( empty( $this->get_clone_identification_timestamp() ) && ( ! fs_is_network_admin() || ! ( $this->is_clone_resolution_options_notice_shown() || $this->is_temporary_duplicate_notice_shown() ) ) ) { $this->hide_clone_admin_notices(); } else if ( ! Freemius::is_cron() && ! Freemius::is_admin_post() ) { $this->try_resolve_clone_automatically(); $this->maybe_show_clone_admin_notice(); add_action( 'admin_footer', array( $this, '_add_clone_resolution_javascript' ) ); } } } } /** * Retrieves the timestamp that was stored when a clone was identified. * * @return int|null */ function get_clone_identification_timestamp() { return $this->get_option( 'clone_identification_timestamp', true ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.1 * * @param string $sdk_last_version */ function maybe_update_clone_resolution_support_flag( $sdk_last_version ) { if ( null !== $this->hide_manual_resolution ) { return; } $this->hide_manual_resolution = ( ! empty( $sdk_last_version ) && version_compare( $sdk_last_version, '2.5.0', '<' ) ); } /** * Stores the time when a clone was identified. */ function store_clone_identification_timestamp() { $this->clone_identification_timestamp = time(); } /** * Retrieves the timestamp for the temporary duplicate mode's expiration. * * @return int */ function get_temporary_duplicate_expiration_timestamp() { $temporary_duplicate_mode_start_timestamp = $this->was_temporary_duplicate_mode_selected() ? $this->temporary_duplicate_mode_selection_timestamp : $this->get_clone_identification_timestamp(); return ( $temporary_duplicate_mode_start_timestamp + self::TEMPORARY_DUPLICATE_PERIOD ); } /** * Determines if the SDK should handle clones. The SDK handles clones only up to 3 times with 3 min interval. * * @return bool */ private function should_handle_clones() { if ( ! isset( $this->request_handler_timestamp ) ) { return true; } if ( $this->request_handler_retries_count >= self::CLONE_RESOLUTION_MAX_RETRIES ) { return false; } // Give the logic that handles clones enough time to finish (it is given 3 minutes for now). return ( time() > ( $this->request_handler_timestamp + self::CLONE_RESOLUTION_MAX_EXECUTION_TIME ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.1 * * @return bool */ function should_hide_manual_resolution() { return ( true === $this->hide_manual_resolution ); } /** * Executes the clones handler logic if it should be executed, i.e., based on the return value of the should_handle_clones() method. * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function maybe_run_clone_resolution() { if ( ! $this->should_handle_clones() ) { return; } $this->request_handler_retries_count = isset( $this->request_handler_retries_count ) ? ( $this->request_handler_retries_count + 1 ) : 1; $this->request_handler_timestamp = time(); $handler_id = ( rand() . microtime() ); $this->request_handler_id = $handler_id; // Add cookies to trigger request with the same user access permissions. $cookies = array(); foreach ( $_COOKIE as $name => $value ) { $cookies[] = new WP_Http_Cookie( array( 'name' => $name, 'value' => $value, ) ); } wp_remote_post( admin_url( 'admin-post.php' ), array( 'method' => 'POST', 'body' => array( 'action' => 'fs_clone_resolution', 'handler_id' => $handler_id, ), 'timeout' => 0.01, 'blocking' => false, 'sslverify' => false, 'cookies' => $cookies, ) ); } /** * Executes the clones handler logic. * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function _handle_clone_resolution() { $handler_id = fs_request_get( 'handler_id' ); if ( empty( $handler_id ) ) { return; } if ( ! isset( $this->request_handler_id ) || $this->request_handler_id !== $handler_id ) { return; } if ( ! $this->try_automatic_resolution() ) { $this->clear_temporary_duplicate_notice_shown_timestamp(); } } #-------------------------------------------------------------------------------- #region Automatic Clone Resolution #-------------------------------------------------------------------------------- /** * @var array All installs cache. */ private $all_installs; /** * Checks if a given instance's install is a clone of another subsite in the network. * * @author Vova Feldman (@svovaf) * * @return FS_Site */ private function find_network_subsite_clone_install( Freemius $instance ) { if ( ! is_multisite() ) { // Not a multi-site network. return null; } if ( ! isset( $this->all_installs ) ) { $this->all_installs = FS_DebugManager::get_all_modules_sites(); } // Check if there's another blog that has the same site. $module_type = $instance->get_module_type(); $sites_by_module_type = ! empty( $this->all_installs[ $module_type ] ) ? $this->all_installs[ $module_type ] : array(); $slug = $instance->get_slug(); $sites_by_slug = ! empty( $sites_by_module_type[ $slug ] ) ? $sites_by_module_type[ $slug ] : array(); $current_blog_id = get_current_blog_id(); $current_install = $instance->get_site(); foreach ( $sites_by_slug as $site ) { if ( $current_install->id == $site->id && $current_blog_id != $site->blog_id ) { // Clone is identical to an install on another subsite in the network. return $site; } } return null; } /** * Tries to find a different install of the context product that is associated with the current URL and loads it. * * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param Freemius $instance * @param string $url * * @return object */ private function find_other_install_by_url( Freemius $instance, $url ) { $result = $instance->get_api_user_scope()->get( "/plugins/{$instance->get_id()}/installs.json?url=" . urlencode( $url ) . "&all=true", true ); $current_install = $instance->get_site(); if ( $instance->is_api_result_object( $result, 'installs' ) ) { foreach ( $result->installs as $install ) { if ( $install->id == $current_install->id ) { continue; } if ( $instance->is_only_premium() && ! FS_Plugin_License::is_valid_id( $install->license_id ) ) { continue; } // When searching for installs by a URL, the API will first strip any paths and search for any matching installs by the subdomain. Therefore, we need to test if there's a match between the current URL and the install's URL before continuing. if ( $url !== fs_strip_url_protocol( untrailingslashit( $install->url ) ) ) { continue; } // Found a different install that is associated with the current URL, load it and replace the current install with it if no updated install is found. return $install; } } return null; } /** * Delete the current install associated with a given instance and opt-in/activate-license to create a fresh install. * * @author Vova Feldman (@svovaf) * @since 2.5.0 * * @param Freemius $instance * @param string|false $license_key * * @return bool TRUE if successfully connected. FALSE if failed and had to restore install from backup. */ private function delete_install_and_connect( Freemius $instance, $license_key = false ) { $user = Freemius::_get_user_by_id( $instance->get_site()->user_id ); $instance->delete_current_install( true ); if ( ! is_object( $user ) ) { // Get logged-in WordPress user. $current_user = Freemius::_get_current_wp_user(); // Find the relevant FS user by email address. $user = Freemius::_get_user_by_email( $current_user->user_email ); } if ( is_object( $user ) ) { // When a clone is found, we prefer to use the same user of the original install for the opt-in. $instance->install_with_user( $user, $license_key, false, false ); } else { // If no user is found, activate with the license. $instance->opt_in( false, false, false, $license_key ); } if ( is_object( $instance->get_site() ) ) { // Install successfully created. return true; } // Restore from backup. $instance->restore_backup_site(); return false; } /** * Try to resolve the clone situation automatically. * * @param Freemius $instance * @param string $current_url * @param bool $is_localhost * @param bool|null $is_clone_of_network_subsite * * @return bool If managed to automatically resolve the clone. */ private function try_resolve_clone_automatically_by_instance( Freemius $instance, $current_url, $is_localhost, $is_clone_of_network_subsite = null ) { // Try to find a different install of the context product that is associated with the current URL. $associated_install = $this->find_other_install_by_url( $instance, $current_url ); if ( is_object( $associated_install ) ) { // Replace the current install with a different install that is associated with the current URL. $instance->store_site( new FS_Site( clone $associated_install ) ); $instance->sync_install( array( 'is_new_site' => true ), true ); return true; } if ( ! $instance->is_premium() ) { // For free products, opt-in with the context user to create new install. return $this->delete_install_and_connect( $instance ); } $license = $instance->_get_license(); $can_activate_license = ( is_object( $license ) && ! $license->is_utilized( $is_localhost ) ); if ( ! $can_activate_license ) { // License can't be activated, therefore, can't be automatically resolved. return false; } if ( ! WP_FS__IS_LOCALHOST_FOR_SERVER && ! $is_localhost ) { $is_clone_of_network_subsite = ( ! is_null( $is_clone_of_network_subsite ) ) ? $is_clone_of_network_subsite : is_object( $this->find_network_subsite_clone_install( $instance ) ); if ( ! $is_clone_of_network_subsite ) { return false; } } // If the site is a clone of another subsite in the network, or a localhost one, try to auto activate the license. return $this->delete_install_and_connect( $instance, $license->secret_key ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ private function try_resolve_clone_automatically() { $clone_action = $this->get_clone_resolution_action_from_config(); if ( ! empty( $clone_action ) ) { $this->try_resolve_clone_automatically_by_config( $clone_action ); return; } $this->try_automatic_resolution(); } /** * Tries to resolve the clone situation automatically based on the config in the wp-config.php file. * * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param string $clone_action */ private function try_resolve_clone_automatically_by_config( $clone_action ) { $fs_instances = array(); if ( self::OPTION_LONG_TERM_DUPLICATE === $clone_action ) { $instances = Freemius::_get_all_instances(); foreach ( $instances as $instance ) { if ( ! $instance->is_registered() ) { continue; } if ( ! $instance->is_clone() ) { continue; } $license = $instance->has_features_enabled_license() ? $instance->_get_license() : null; if ( is_object( $license ) && ! $license->is_utilized( ( WP_FS__IS_LOCALHOST_FOR_SERVER || FS_Site::is_localhost_by_address( Freemius::get_unfiltered_site_url() ) ) ) ) { $fs_instances[] = $instance; } } if ( empty( $fs_instances ) ) { return; } } $this->resolve_cloned_sites( $clone_action, $fs_instances ); } /** * @author Leo Fajard (@leorw) * @since 2.5.0 * * @return string|null */ private function get_clone_resolution_action_from_config() { if ( ! defined( 'FS__RESOLVE_CLONE_AS' ) ) { return null; } if ( ! in_array( FS__RESOLVE_CLONE_AS, array( self::OPTION_NEW_HOME, self::OPTION_TEMPORARY_DUPLICATE, self::OPTION_LONG_TERM_DUPLICATE, ) ) ) { return null; } return FS__RESOLVE_CLONE_AS; } /** * Tries to recover the install of a newly created subsite or resolve it if it's a clone. * * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param Freemius $instance */ function maybe_resolve_new_subsite_install_automatically( Freemius $instance ) { if ( ! $instance->is_user_in_admin() ) { // Try to recover an install or resolve a clone only when there's a user in admin to prevent doing it prematurely (e.g., the install can get replaced with clone data again). return; } if ( ! is_multisite() ) { return; } $new_blog_install_map = $this->new_blog_install_map; if ( empty( $new_blog_install_map ) || ! is_array( $new_blog_install_map ) ) { return; } $is_network_admin = fs_is_network_admin(); if ( ! $is_network_admin ) { // If not in network admin, handle the current site. $blog_id = get_current_blog_id(); } else { // If in network admin, handle only the first site. $blog_ids = array_keys( $new_blog_install_map ); $blog_id = $blog_ids[0]; } if ( ! isset( $new_blog_install_map[ $blog_id ] ) ) { // There's no site to handle. return; } $expected_install_id = $new_blog_install_map[ $blog_id ]['install_id']; $current_install = $instance->get_install_by_blog_id( $blog_id ); $current_install_id = is_object( $current_install ) ? $current_install->id : null; if ( $expected_install_id == $current_install_id ) { // Remove the current site's information from the map to prevent handling it again. $this->remove_new_blog_install_info_from_storage( $blog_id ); return; } require_once WP_FS__DIR_INCLUDES . '/class-fs-lock.php'; $lock = new FS_Lock( self::OPTION_NAME . '_subsite' ); if ( ! $lock->try_lock(60) ) { return; } $instance->switch_to_blog( $blog_id ); $current_url = untrailingslashit( Freemius::get_unfiltered_site_url( null, true ) ); $current_install_url = is_object( $current_install ) ? fs_strip_url_protocol( untrailingslashit( $current_install->url ) ) : null; // This can be `false` even if the install is a clone as the URL can be updated as part of the cloning process. $is_clone = ( ! is_null( $current_install_url ) && $current_url !== $current_install_url ); if ( ! FS_Site::is_valid_id( $expected_install_id ) ) { $expected_install = null; } else { $expected_install = $instance->fetch_install_by_id( $expected_install_id ); } if ( FS_Api::is_api_result_entity( $expected_install ) ) { // Replace the current install with the expected install. $instance->store_site( new FS_Site( clone $expected_install ) ); $instance->sync_install( array( 'is_new_site' => true ), true ); } else { $network_subsite_clone_install = null; if ( ! $is_clone ) { // It is possible that `$is_clone` is `false` but the install is actually a clone as the following call checks the install ID and not the URL. $network_subsite_clone_install = $this->find_network_subsite_clone_install( $instance ); } if ( $is_clone || is_object( $network_subsite_clone_install ) ) { // If there's no expected install (or it couldn't be fetched) and the current install is a clone, try to resolve the clone automatically. $is_localhost = FS_Site::is_localhost_by_address( $current_url ); $resolved = $this->try_resolve_clone_automatically_by_instance( $instance, $current_url, $is_localhost, is_object( $network_subsite_clone_install ) ); if ( ! $resolved && is_object( $network_subsite_clone_install ) ) { if ( empty( $this->get_clone_identification_timestamp() ) ) { $this->store_clone_identification_timestamp(); } // Since the clone couldn't be identified based on the URL, replace the stored install with the cloned install so that the manual clone resolution notice will appear. $instance->store_site( clone $network_subsite_clone_install ); } } } $instance->restore_current_blog(); // Remove the current site's information from the map to prevent handling it again. $this->remove_new_blog_install_info_from_storage( $blog_id ); $lock->unlock(); } /** * If a new install was created after creating a new subsite, its ID is stored in the blog-install map so that it can be recovered in case it's replaced with a clone install (e.g., when the newly created subsite is a clone). The IDs of the clone subsites that were created while not running this version of the SDK or a higher version will also be stored in the said map so that the clone manager can also try to resolve them later on. * * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param int $blog_id * @param FS_Site $site */ function store_blog_install_info( $blog_id, $site = null ) { $new_blog_install_map = $this->new_blog_install_map; if ( empty( $new_blog_install_map ) || ! is_array( $new_blog_install_map ) ) { $new_blog_install_map = array(); } $install_id = null; if ( is_object( $site ) ) { $install_id = $site->id; } $new_blog_install_map[ $blog_id ] = array( 'install_id' => $install_id ); $this->new_blog_install_map = $new_blog_install_map; } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param int $blog_id */ private function remove_new_blog_install_info_from_storage( $blog_id ) { $new_blog_install_map = $this->new_blog_install_map; unset( $new_blog_install_map[ $blog_id ] ); $this->new_blog_install_map = $new_blog_install_map; } /** * Tries to resolve all clones automatically. * * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @return bool If managed to automatically resolve all clones. */ private function try_automatic_resolution() { $this->_logger->entrance(); require_once WP_FS__DIR_INCLUDES . '/class-fs-lock.php'; $lock = new FS_Lock( self::OPTION_NAME ); /** * Try to acquire lock for the next 60 sec based on the thread ID. */ if ( ! $lock->try_lock( 60 ) ) { return false; } $current_url = untrailingslashit( Freemius::get_unfiltered_site_url( null, true ) ); $is_localhost = FS_Site::is_localhost_by_address( $current_url ); $require_manual_resolution = false; $instances = Freemius::_get_all_instances(); foreach ( $instances as $instance ) { if ( ! $instance->is_registered() ) { continue; } if ( ! $instance->is_clone() ) { continue; } if ( ! $this->try_resolve_clone_automatically_by_instance( $instance, $current_url, $is_localhost ) ) { $require_manual_resolution = true; } } // Create a 1-day lock. $lock->lock( WP_FS__TIME_24_HOURS_IN_SEC ); return ( ! $require_manual_resolution ); } #endregion #-------------------------------------------------------------------------------- #region Manual Clone Resolution #-------------------------------------------------------------------------------- /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function _add_clone_resolution_javascript() { $vars = array( 'ajax_action' => Freemius::get_ajax_action_static( 'handle_clone_resolution' ) ); fs_require_once_template( 'clone-resolution-js.php', $vars ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function _clone_resolution_action_ajax_handler() { $this->_logger->entrance(); check_ajax_referer( Freemius::get_ajax_action_static( 'handle_clone_resolution' ), 'security' ); $clone_action = fs_request_get( 'clone_action' ); $blog_id = is_multisite() ? fs_request_get( 'blog_id' ) : 0; if ( is_multisite() && $blog_id == get_current_blog_id() ) { $blog_id = 0; } if ( empty( $clone_action ) ) { Freemius::shoot_ajax_failure( array( 'message' => fs_text_inline( 'Invalid clone resolution action.', 'invalid-clone-resolution-action-error' ), 'redirect_url' => '', ) ); } $result = $this->resolve_cloned_sites( $clone_action, array(), $blog_id ); Freemius::shoot_ajax_success( $result ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param string $clone_action * @param Freemius[] $fs_instances * @param int $blog_id * * @return array */ private function resolve_cloned_sites( $clone_action, $fs_instances = array(), $blog_id = 0 ) { $this->_logger->entrance(); $result = array(); $instances_with_clone = array(); $instances_with_clone_count = 0; $install_by_instance_id = array(); $instances = ( ! empty( $fs_instances ) ) ? $fs_instances : Freemius::_get_all_instances(); $should_switch_to_blog = ( $blog_id > 0 ); foreach ( $instances as $instance ) { if ( $should_switch_to_blog ) { $instance->switch_to_blog( $blog_id ); } if ( $instance->is_registered() && $instance->is_clone() ) { $instances_with_clone[] = $instance; $instances_with_clone_count ++; $install_by_instance_id[ $instance->get_id() ] = $instance->get_site(); } } if ( self::OPTION_TEMPORARY_DUPLICATE === $clone_action ) { $this->store_temporary_duplicate_timestamp(); } else { $redirect_url = ''; foreach ( $instances_with_clone as $instance ) { if ( $should_switch_to_blog ) { $instance->switch_to_blog( $blog_id ); } $has_error = false; if ( self::OPTION_NEW_HOME === $clone_action ) { $instance->sync_install( array( 'is_new_site' => true ), true ); if ( $instance->is_clone() ) { $has_error = true; } } else { $instance->_handle_long_term_duplicate(); if ( ! is_object( $instance->get_site() ) ) { $has_error = true; } } if ( $has_error && 1 === $instances_with_clone_count ) { $redirect_url = $instance->get_activation_url(); } } $result = ( array( 'redirect_url' => $redirect_url ) ); } foreach ( $instances_with_clone as $instance ) { if ( $should_switch_to_blog ) { $instance->switch_to_blog( $blog_id ); } // No longer a clone, send an update. if ( ! $instance->is_clone() ) { $instance->send_clone_resolution_update( $clone_action, $install_by_instance_id[ $instance->get_id() ] ); } } if ( 'temporary_duplicate_license_activation' !== $clone_action ) { $this->remove_clone_resolution_options_notice(); } else { $this->remove_temporary_duplicate_notice(); } if ( $should_switch_to_blog ) { foreach ( $instances as $instance ) { $instance->restore_current_blog(); } } return $result; } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ private function hide_clone_admin_notices() { $this->remove_clone_resolution_options_notice( false ); $this->remove_temporary_duplicate_notice( false ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function maybe_show_clone_admin_notice() { $this->_logger->entrance(); if ( fs_is_network_admin() ) { $existing_notice_ids = $this->maybe_remove_notices(); if ( ! empty( $existing_notice_ids ) ) { fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' ); } return; } $first_instance_with_clone = null; $site_urls = array(); $sites_with_license_urls = array(); $sites_with_premium_version_count = 0; $product_ids = array(); $product_titles = array(); $instances = Freemius::_get_all_instances(); foreach ( $instances as $instance ) { if ( ! $instance->is_registered() ) { continue; } if ( ! $instance->is_clone( true ) ) { continue; } $install = $instance->get_site(); $site_urls[] = $install->url; $product_ids[] = $instance->get_id(); $product_titles[] = $instance->get_plugin_title(); if ( is_null( $first_instance_with_clone ) ) { $first_instance_with_clone = $instance; } if ( is_object( $instance->_get_license() ) ) { $sites_with_license_urls[] = $install->url; } if ( $instance->is_premium() ) { $sites_with_premium_version_count ++; } } if ( empty( $site_urls ) && empty( $sites_with_license_urls ) ) { $this->hide_clone_admin_notices(); return; } $site_urls = array_unique( $site_urls ); $sites_with_license_urls = array_unique( $sites_with_license_urls ); $module_label = fs_text_inline( 'products', 'products' ); $admin_notice_module_title = null; $has_temporary_duplicate_mode_expired = $this->has_temporary_duplicate_mode_expired(); if ( ! $this->was_temporary_duplicate_mode_selected() || $has_temporary_duplicate_mode_expired ) { if ( ! empty( $site_urls ) ) { fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' ); $doc_url = 'https://freemius.com/help/documentation/wordpress-sdk/safe-mode-clone-resolution-duplicate-website/'; if ( 1 === count( $instances ) ) { $doc_url = fs_apply_filter( $first_instance_with_clone->get_unique_affix(), 'clone_resolution_documentation_url', $doc_url ); } $this->add_manual_clone_resolution_admin_notice( $product_ids, $product_titles, $site_urls, Freemius::get_unfiltered_site_url(), ( count( $site_urls ) === count( $sites_with_license_urls ) ), ( count( $site_urls ) === $sites_with_premium_version_count ), $doc_url ); } return; } if ( empty( $sites_with_license_urls ) ) { return; } if ( ! $this->is_temporary_duplicate_notice_shown() ) { $last_time_temporary_duplicate_notice_shown = $this->temporary_duplicate_notice_shown_timestamp; $was_temporary_duplicate_notice_shown_before = is_numeric( $last_time_temporary_duplicate_notice_shown ); if ( $was_temporary_duplicate_notice_shown_before ) { $temporary_duplicate_mode_expiration_timestamp = $this->get_temporary_duplicate_expiration_timestamp(); $current_time = time(); if ( $current_time > $temporary_duplicate_mode_expiration_timestamp || $current_time < ( $temporary_duplicate_mode_expiration_timestamp - ( 2 * WP_FS__TIME_24_HOURS_IN_SEC ) ) ) { // Do not show the notice if the temporary duplicate mode has already expired or it will expire more than 2 days from now. return; } } } if ( 1 === count( $sites_with_license_urls ) ) { $module_label = $first_instance_with_clone->get_module_label( true ); $admin_notice_module_title = $first_instance_with_clone->get_plugin_title(); } fs_enqueue_local_style( 'fs_clone_resolution_notice', '/admin/clone-resolution.css' ); $this->add_temporary_duplicate_sticky_notice( $product_ids, $this->get_temporary_duplicate_admin_notice_string( $sites_with_license_urls, $product_titles, $module_label ), $admin_notice_module_title ); } /** * Removes the notices from the storage if the context product is either no longer active on the context subsite or it's active but there's no longer any clone. This prevents the notices from being shown on the network-level admin page when they are no longer relevant. * * @author Leo Fajardo (@leorw) * @since 2.5.1 * * @return string[] */ private function maybe_remove_notices() { $notices = array( 'clone_resolution_options_notice' => $this->_notices->get_sticky( 'clone_resolution_options_notice', true ), 'temporary_duplicate_notice' => $this->_notices->get_sticky( 'temporary_duplicate_notice', true ), ); $instances = Freemius::_get_all_instances(); foreach ( $notices as $id => $notice ) { if ( ! is_array( $notice ) ) { unset( $notices[ $id ] ); continue; } if ( empty( $notice['data'] ) || ! is_array( $notice['data'] ) ) { continue; } if ( empty( $notice['data']['product_ids'] ) || empty( $notice['data']['blog_id'] ) ) { continue; } $product_ids = $notice['data']['product_ids']; $blog_id = $notice['data']['blog_id']; $has_clone = false; if ( ! is_null( get_site( $blog_id ) ) ) { foreach ( $product_ids as $product_id ) { if ( ! isset( $instances[ 'm_' . $product_id ] ) ) { continue; } $instance = $instances[ 'm_' . $product_id ]; $plugin_basename = $instance->get_plugin_basename(); $is_plugin_active = is_plugin_active_for_network( $plugin_basename ); if ( ! $is_plugin_active ) { switch_to_blog( $blog_id ); $is_plugin_active = is_plugin_active( $plugin_basename ); restore_current_blog(); } if ( ! $is_plugin_active ) { continue; } $install = $instance->get_install_by_blog_id( $blog_id ); if ( ! is_object( $install ) ) { continue; } $subsite_url = Freemius::get_unfiltered_site_url( $blog_id, true, true ); $has_clone = ( fs_strip_url_protocol( trailingslashit( $install->url ) ) !== $subsite_url ); } } if ( ! $has_clone ) { $this->_notices->remove_sticky( $id, true, false ); unset( $notices[ $id ] ); } } return array_keys( $notices ); } /** * Adds a notice that provides the logged-in WordPress user with manual clone resolution options. * * @param number[] $product_ids * @param string[] $site_urls * @param string $current_url * @param bool $has_license * @param bool $is_premium * @param string $doc_url */ private function add_manual_clone_resolution_admin_notice( $product_ids, $product_titles, $site_urls, $current_url, $has_license, $is_premium, $doc_url ) { $this->_logger->entrance(); $total_sites = count( $site_urls ); $sites_list = ''; $total_products = count( $product_titles ); $products_list = ''; if ( 1 === $total_products ) { $notice_header = sprintf( '

    %s

    ', fs_esc_html_inline( '%1$s has been placed into safe mode because we noticed that %2$s is an exact copy of %3$s.', 'single-cloned-site-safe-mode-message' ) ); } else { $notice_header = sprintf( '

    %s

    ', ( 1 === $total_sites ) ? fs_esc_html_inline( 'The products below have been placed into safe mode because we noticed that %2$s is an exact copy of %3$s:%1$s', 'multiple-products-cloned-site-safe-mode-message' ) : fs_esc_html_inline( 'The products below have been placed into safe mode because we noticed that %2$s is an exact copy of these sites:%3$s%1$s', 'multiple-products-multiple-cloned-sites-safe-mode-message' ) ); foreach ( $product_titles as $product_title ) { $products_list .= sprintf( '
  • %s
  • ', $product_title ); } $products_list = '
      ' . $products_list . '
    '; foreach ( $site_urls as $site_url ) { $sites_list .= sprintf( '
  • %s
  • ', $site_url, fs_strip_url_protocol( $site_url ) ); } $sites_list = '
      ' . $sites_list . '
    '; } $remote_site_link = '' . (1 === $total_sites ? sprintf( '%s', $site_urls[0], fs_strip_url_protocol( $site_urls[0] ) ) : fs_text_inline( 'the above-mentioned sites', 'above-mentioned-sites' )) . ''; $current_site_link = sprintf( '%s', $current_url, fs_strip_url_protocol( $current_url ) ); $button_template = ''; $option_template = '
    %s

    %s

    %s
    '; $duplicate_option = sprintf( $option_template, fs_esc_html_inline( 'Is %2$s a duplicate of %4$s?', 'duplicate-site-confirmation-message' ), fs_esc_html_inline( 'Yes, %2$s is a duplicate of %4$s for the purpose of testing, staging, or development.', 'duplicate-site-message' ), ( $this->has_temporary_duplicate_mode_expired() ? sprintf( $button_template, 'long_term_duplicate', fs_text_inline( 'Long-Term Duplicate', 'long-term-duplicate' ) ) : sprintf( $button_template, 'temporary_duplicate', fs_text_inline( 'Duplicate Website', 'duplicate-site' ) ) ) ); $migration_option = sprintf( $option_template, fs_esc_html_inline( 'Is %2$s the new home of %4$s?', 'migrate-site-confirmation-message' ), sprintf( fs_esc_html_inline( 'Yes, %%2$s is replacing %%4$s. I would like to migrate my %s from %%4$s to %%2$s.', 'migrate-site-message' ), ( $has_license ? fs_text_inline( 'license', 'license' ) : fs_text_inline( 'data', 'data' ) ) ), sprintf( $button_template, 'new_home', $has_license ? fs_text_inline( 'Migrate License', 'migrate-product-license' ) : fs_text_inline( 'Migrate', 'migrate-product-data' ) ) ); $new_website = sprintf( $option_template, fs_esc_html_inline( 'Is %2$s a new website?', 'new-site-confirmation-message' ), fs_esc_html_inline( 'Yes, %2$s is a new and different website that is separate from %4$s.', 'new-site-message' ) . ($is_premium ? ' ' . fs_text_inline( 'It requires license activation.', 'new-site-requires-license-activation-message' ) : '' ), sprintf( $button_template, 'new_website', ( ! $is_premium || ! $has_license ) ? fs_text_inline( 'New Website', 'new-website' ) : fs_text_inline( 'Activate License', 'activate-license' ) ) ); $blog_id = get_current_blog_id(); /** * %1$s - single product's title or product titles list. * %2$s - site's URL. * %3$s - single install's URL or install URLs list. * %4$s - Clone site's link or "the above-mentioned sites" if there are multiple clone sites. */ $message = sprintf( $notice_header . '
    ' . $duplicate_option . $migration_option . $new_website . '
    ' . sprintf( '
    Unsure what to do? Read more here.
    ', $doc_url ), // %1$s ( 1 === $total_products ? sprintf( '%s', $product_titles[0] ) : ( 1 === $total_sites ? sprintf( '
    %s
    ', $products_list ) : sprintf( '

    %s:

    %s
    ', fs_esc_html_x_inline( 'Products', 'Clone resolution admin notice products list label', 'products' ), $products_list ) ) ), // %2$s $current_site_link, // %3$s ( 1 === $total_sites ? $remote_site_link : $sites_list ), // %4$s $remote_site_link ); $this->_notices->add_sticky( $message, 'clone_resolution_options_notice', '', 'warn', true, null, null, true, // Intentionally not dismissible. false, array( 'product_ids' => $product_ids, 'blog_id' => $blog_id ) ); } #endregion #-------------------------------------------------------------------------------- #region Temporary Duplicate (Short Term) #-------------------------------------------------------------------------------- /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @return string */ private function get_temporary_duplicate_admin_notice_string( $site_urls, $product_titles, $module_label ) { $this->_logger->entrance(); $temporary_duplicate_end_date = $this->get_temporary_duplicate_expiration_timestamp(); $temporary_duplicate_end_date = date( 'M j, Y', $temporary_duplicate_end_date ); $current_url = Freemius::get_unfiltered_site_url(); $current_site_link = sprintf( '%s', $current_url, fs_strip_url_protocol( $current_url ) ); $total_sites = count( $site_urls ); $sites_list = ''; $total_products = count( $product_titles ); $products_list = ''; if ( $total_sites > 1 ) { foreach ( $site_urls as $site_url ) { $sites_list .= sprintf( '
  • %s
  • ', $site_url, fs_strip_url_protocol( $site_url ) ); } $sites_list = '
      ' . $sites_list . '
    '; } if ( $total_products > 1 ) { foreach ( $product_titles as $product_title ) { $products_list .= sprintf( '
  • %s
  • ', $product_title ); } $products_list = '
      ' . $products_list . '
    '; } return sprintf( sprintf( '
    %s
    ', ( 1 === $total_sites ? sprintf( '

    %s

    ', fs_esc_html_inline( 'You marked this website, %s, as a temporary duplicate of %s.', 'temporary-duplicate-message' ) ) : sprintf( '

    %s:

    ', fs_esc_html_inline( 'You marked this website, %s, as a temporary duplicate of these sites', 'temporary-duplicate-of-sites-message' ) ) . '%s' ) ) . '%s', $current_site_link, ( 1 === $total_sites ? sprintf( '%s', $site_urls[0], fs_strip_url_protocol( $site_urls[0] ) ) : $sites_list ), sprintf( '

    %s

    %s

    %s

    ', esc_attr( admin_url( 'admin-ajax.php?_fs_network_admin=false', 'relative' ) ), sprintf( fs_esc_html_inline( "%s automatic security & feature updates and paid functionality will keep working without interruptions until %s (or when your license expires, whatever comes first).", 'duplicate-site-confirmation-message' ), ( 1 === $total_products ? sprintf( fs_esc_html_x_inline( "The %s's", '"The ", e.g.: "The plugin"', 'the-product-x'), "{$module_label}" ) : fs_esc_html_inline( "The following products'", 'the-following-products' ) ), sprintf( '%s', $temporary_duplicate_end_date ) ), ( 1 === $total_products ? '' : sprintf( '
    %s
    ', $products_list ) ), sprintf( fs_esc_html_inline( 'If this is a long term duplicate, to keep automatic updates and paid functionality after %s, please %s.', 'duplicate-site-message' ), sprintf( '%s', $temporary_duplicate_end_date), sprintf( '%s', fs_esc_html_inline( 'activate a license here', 'activate-license-here' ) ) ) ) ); } /** * Determines if the temporary duplicate mode has already expired. * * @return bool */ function has_temporary_duplicate_mode_expired() { $temporary_duplicate_mode_start_timestamp = $this->was_temporary_duplicate_mode_selected() ? $this->get_option( 'temporary_duplicate_mode_selection_timestamp', true ) : $this->get_clone_identification_timestamp(); if ( ! is_numeric( $temporary_duplicate_mode_start_timestamp ) ) { return false; } return ( time() > ( $temporary_duplicate_mode_start_timestamp + self::TEMPORARY_DUPLICATE_PERIOD ) ); } /** * Determines if the logged-in WordPress user manually selected the temporary duplicate mode for the site. * * @return bool */ function was_temporary_duplicate_mode_selected() { return is_numeric( $this->temporary_duplicate_mode_selection_timestamp ); } /** * Stores the time when the logged-in WordPress user selected the temporary duplicate mode for the site. */ private function store_temporary_duplicate_timestamp() { $this->temporary_duplicate_mode_selection_timestamp = time(); } /** * Removes the notice that is shown when the logged-in WordPress user has selected the temporary duplicate mode for the site. * * @param bool $store */ function remove_clone_resolution_options_notice( $store = true ) { $this->_notices->remove_sticky( 'clone_resolution_options_notice', true, $store ); } /** * Removes the notice that is shown when the logged-in WordPress user has selected the temporary duplicate mode for the site. * * @param bool $store */ function remove_temporary_duplicate_notice( $store = true ) { $this->_notices->remove_sticky( 'temporary_duplicate_notice', true, $store ); } /** * Determines if the manual clone resolution options notice is currently being shown. * * @return bool */ function is_clone_resolution_options_notice_shown() { return $this->_notices->has_sticky( 'clone_resolution_options_notice', true ); } /** * Determines if the temporary duplicate notice is currently being shown. * * @return bool */ function is_temporary_duplicate_notice_shown() { return $this->_notices->has_sticky( 'temporary_duplicate_notice', true ); } /** * Determines if a site was marked as a temporary duplicate and if it's still a temporary duplicate. * * @return bool */ function is_temporary_duplicate_by_blog_id( $blog_id ) { $timestamp = $this->get_option( 'temporary_duplicate_mode_selection_timestamp', false, $blog_id ); return ( is_numeric( $timestamp ) && time() < ( $timestamp + self::TEMPORARY_DUPLICATE_PERIOD ) ); } /** * Determines the last time the temporary duplicate notice was shown. * * @return int|null */ function last_time_temporary_duplicate_notice_was_shown() { return $this->temporary_duplicate_notice_shown_timestamp; } /** * Clears the time that has been stored when the temporary duplicate notice was shown. */ function clear_temporary_duplicate_notice_shown_timestamp() { unset( $this->temporary_duplicate_notice_shown_timestamp ); } /** * Adds a temporary duplicate notice that provides the logged-in WordPress user with an option to activate a license for the site. * * @param number[] $product_ids * @param string $message * @param string|null $plugin_title */ function add_temporary_duplicate_sticky_notice( $product_ids, $message, $plugin_title = null ) { $this->_logger->entrance(); $this->_notices->add_sticky( $message, 'temporary_duplicate_notice', '', 'promotion', true, null, $plugin_title, true, true, array( 'product_ids' => $product_ids, 'blog_id' => get_current_blog_id() ) ); $this->temporary_duplicate_notice_shown_timestamp = time(); } #endregion /** * @author Leo Fajardo * @since 2.5.0 * * @param string $key * * @return bool */ private function should_use_network_storage( $key ) { return ( 'new_blog_install_map' === $key ); } /** * @param string $key * @param number|null $blog_id * * @return FS_Option_Manager */ private function get_storage( $key, $blog_id = null ) { if ( is_numeric( $blog_id ) ){ return FS_Option_Manager::get_manager( WP_FS___OPTION_PREFIX . self::OPTION_MANAGER_NAME, true, $blog_id ); } return $this->should_use_network_storage( $key ) ? $this->_network_storage : $this->_storage; } /** * @param string $name * @param bool $flush * @param number|null $blog_id * * @return mixed */ private function get_option( $name, $flush = false, $blog_id = null ) { return $this->get_storage( $name, $blog_id )->get_option( $name, null, $flush ); } #-------------------------------------------------------------------------------- #region Magic methods #-------------------------------------------------------------------------------- /** * @param string $name * @param int|string $value */ function __set( $name, $value ) { $this->get_storage( $name )->set_option( $name, $value, true ); } /** * @param string $name * * @return bool */ function __isset( $name ) { return $this->get_storage( $name )->has_option( $name, true ); } /** * @param string $name */ function __unset( $name ) { $this->get_storage( $name )->unset_option( $name, true ); } /** * @param string $name * * @return null|int|string */ function __get( $name ) { return $this->get_option( $name, // Reload storage from DB when accessing request_handler_* options to avoid race conditions. fs_starts_with( $name, 'request_handler' ) ); } #endregion } freemius/includes/managers/class-fs-checkout-manager.php000064400000015054147600046700017436 0ustar00 $fs->get_id(), 'public_key' => $fs->get_public_key(), 'plugin_version' => $fs->get_plugin_version(), 'mode' => 'dashboard', 'trial' => fs_request_get_bool( 'trial' ), 'is_ms' => ( fs_is_network_admin() && $fs->is_network_active() ), ); if ( FS_Plugin_Plan::is_valid_id( $plan_id ) ) { $context_params['plan_id'] = $plan_id; } if ( $licenses === strval( intval( $licenses ) ) && $licenses > 0 ) { $context_params['licenses'] = $licenses; } if ( $plugin_id == $fs->get_id() ) { $is_premium = $fs->is_premium(); $bundle_id = $fs->get_bundle_id(); if ( ! is_null( $bundle_id ) ) { $context_params['bundle_id'] = $bundle_id; } } else { // Identify the module code version of the checkout context module. if ( $fs->is_addon_activated( $plugin_id ) ) { $fs_addon = Freemius::get_instance_by_id( $plugin_id ); $is_premium = $fs_addon->is_premium(); } else { // If add-on isn't activated assume the premium version isn't installed. $is_premium = false; } } // Get site context secure params. if ( $fs->is_registered() ) { $site = $fs->get_site(); if ( $plugin_id != $fs->get_id() ) { if ( $fs->is_addon_activated( $plugin_id ) ) { $fs_addon = Freemius::get_instance_by_id( $plugin_id ); $addon_site = $fs_addon->get_site(); if ( is_object( $addon_site ) ) { $site = $addon_site; } } } $context_params = array_merge( $context_params, FS_Security::instance()->get_context_params( $site, $timestamp, 'checkout' ) ); } else { $current_user = Freemius::_get_current_wp_user(); // Add site and user info to the request, this information // is NOT being stored unless the user complete the purchase // and agrees to the TOS. $context_params = array_merge( $context_params, array( 'user_firstname' => $current_user->user_firstname, 'user_lastname' => $current_user->user_lastname, 'user_email' => $current_user->user_email, 'home_url' => home_url(), ) ); $fs_user = Freemius::_get_user_by_email( $current_user->user_email ); if ( is_object( $fs_user ) && $fs_user->is_verified() ) { $context_params = array_merge( $context_params, FS_Security::instance()->get_context_params( $fs_user, $timestamp, 'checkout' ) ); } } if ( $fs->is_payments_sandbox() ) { // Append plugin secure token for sandbox mode authentication. $context_params['sandbox'] = FS_Security::instance()->get_secure_token( $fs->get_plugin(), $timestamp, 'checkout' ); /** * @since 1.1.7.3 Add security timestamp for sandbox even for anonymous user. */ if ( empty( $context_params['s_ctx_ts'] ) ) { $context_params['s_ctx_ts'] = $timestamp; } } $can_user_install = ( ( $fs->is_plugin() && current_user_can( 'install_plugins' ) ) || ( $fs->is_theme() && current_user_can( 'install_themes' ) ) ); return array_merge( $context_params, $_GET, array( // Current plugin version. 'plugin_version' => $fs->get_plugin_version(), 'sdk_version' => WP_FS__SDK_VERSION, 'is_premium' => $is_premium ? 'true' : 'false', 'can_install' => $can_user_install ? 'true' : 'false', ) ); } /** * The return URL to pass to the checkout when the checkout is loaded in "redirect" mode. * * @param Freemius $fs * * @return string */ public function get_checkout_redirect_return_url( Freemius $fs ) { $request_url = remove_query_arg( '_wp_http_referer' ); return fs_nonce_url( $fs->checkout_url( fs_request_get( 'billing_cycle' ), fs_request_get_bool( 'trial' ), array( 'process_redirect' => 'true', '_wp_http_referer' => $request_url, ) ), $this->get_checkout_redirect_nonce_action( $fs ) ); } /** * @param array $query_params * @param string $base_url * * @return string */ public function get_full_checkout_url( array $query_params, $base_url = FS_CHECKOUT__ADDRESS ) { return $base_url . '/?' . http_build_query( $query_params ); } /** * Verifies the redirect after a checkout with the nonce. * * @param Freemius $fs */ public function verify_checkout_redirect_nonce( Freemius $fs ) { check_admin_referer( $this->get_checkout_redirect_nonce_action( $fs ) ); } /** * Get the URL to process a new install after the checkout. * * @param Freemius $fs * @param number $plugin_id * * @return string */ public function get_install_url( Freemius $fs, $plugin_id ) { return fs_nonce_url( $fs->_get_admin_page_url( 'account', array( 'fs_action' => $fs->get_unique_affix() . '_activate_new', 'plugin_id' => $plugin_id, ) ), $fs->get_unique_affix() . '_activate_new' ); } /** * Get the URL to process a pending activation after the checkout. * * @param Freemius $fs * @param number $plugin_id * * @return string */ public function get_pending_activation_url( Freemius $fs, $plugin_id ) { return fs_nonce_url( $fs->_get_admin_page_url( 'account', array( 'fs_action' => $fs->get_unique_affix() . '_activate_new', 'plugin_id' => $plugin_id, 'pending_activation' => true, 'has_upgrade_context' => true, ) ), $fs->get_unique_affix() . '_activate_new' ); } private function get_checkout_redirect_nonce_action( Freemius $fs ) { return $fs->get_unique_affix() . '_checkout_redirect'; } }freemius/includes/managers/class-fs-cache-manager.php000064400000022342147600046700016672 0ustar00_logger = FS_Logger::get_logger( WP_FS__SLUG . '_cach_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); $this->_logger->entrance(); $this->_logger->log( 'id = ' . $id ); $this->_options = FS_Option_Manager::get_manager( $id, true, true, false ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param $id * * @return FS_Cache_Manager */ static function get_manager( $id ) { $id = strtolower( $id ); if ( ! isset( self::$_MANAGERS[ $id ] ) ) { self::$_MANAGERS[ $id ] = new FS_Cache_Manager( $id ); } return self::$_MANAGERS[ $id ]; } /** * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @return bool */ function is_empty() { $this->_logger->entrance(); return $this->_options->is_empty(); } /** * @author Vova Feldman (@svovaf) * @since 1.1.6 */ function clear() { $this->_logger->entrance(); $this->_options->clear( true ); } /** * Delete cache manager from DB. * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ function delete() { $this->_options->delete(); } /** * Check if there's a cached item. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $key * * @return bool */ function has( $key ) { $cache_entry = $this->_options->get_option( $key, false ); return ( is_object( $cache_entry ) && isset( $cache_entry->timestamp ) && is_numeric( $cache_entry->timestamp ) ); } /** * Check if there's a valid cached item. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $key * @param null|int $expiration Since 1.2.2.7 * * @return bool */ function has_valid( $key, $expiration = null ) { $cache_entry = $this->_options->get_option( $key, false ); $is_valid = ( is_object( $cache_entry ) && isset( $cache_entry->timestamp ) && is_numeric( $cache_entry->timestamp ) && $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME ); if ( $is_valid && is_numeric( $expiration ) && isset( $cache_entry->created ) && is_numeric( $cache_entry->created ) && $cache_entry->created + $expiration < WP_FS__SCRIPT_START_TIME ) { /** * Even if the cache is still valid, since we are checking for validity * with an explicit expiration period, if the period has past, return * `false` as if the cache is invalid. * * @since 1.2.2.7 */ $is_valid = false; } return $is_valid; } /** * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $key * @param mixed $default * * @return mixed */ function get( $key, $default = null ) { $this->_logger->entrance( 'key = ' . $key ); $cache_entry = $this->_options->get_option( $key, false ); if ( is_object( $cache_entry ) && isset( $cache_entry->timestamp ) && is_numeric( $cache_entry->timestamp ) ) { return $cache_entry->result; } return is_object( $default ) ? clone $default : $default; } /** * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $key * @param mixed $default * * @return mixed */ function get_valid( $key, $default = null ) { $this->_logger->entrance( 'key = ' . $key ); $cache_entry = $this->_options->get_option( $key, false ); if ( is_object( $cache_entry ) && isset( $cache_entry->timestamp ) && is_numeric( $cache_entry->timestamp ) && $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME ) { return $cache_entry->result; } return is_object( $default ) ? clone $default : $default; } /** * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $key * @param mixed $value * @param int $expiration * @param int $created Since 2.0.0 Cache creation date. */ function set( $key, $value, $expiration = WP_FS__TIME_24_HOURS_IN_SEC, $created = WP_FS__SCRIPT_START_TIME ) { $this->_logger->entrance( 'key = ' . $key ); $cache_entry = new stdClass(); $cache_entry->result = $value; $cache_entry->created = $created; $cache_entry->timestamp = $created + $expiration; $this->_options->set_option( $key, $cache_entry, true ); } /** * Get cached record expiration, or false if not cached or expired. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @param string $key * * @return bool|int */ function get_record_expiration( $key ) { $this->_logger->entrance( 'key = ' . $key ); $cache_entry = $this->_options->get_option( $key, false ); if ( is_object( $cache_entry ) && isset( $cache_entry->timestamp ) && is_numeric( $cache_entry->timestamp ) && $cache_entry->timestamp > WP_FS__SCRIPT_START_TIME ) { return $cache_entry->timestamp; } return false; } /** * Purge cached item. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $key */ function purge( $key ) { $this->_logger->entrance( 'key = ' . $key ); $this->_options->unset_option( $key, true ); } /** * Extend cached item caching period. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $key * @param int $expiration * * @return bool */ function update_expiration( $key, $expiration = WP_FS__TIME_24_HOURS_IN_SEC ) { $this->_logger->entrance( 'key = ' . $key ); $cache_entry = $this->_options->get_option( $key, false ); if ( ! is_object( $cache_entry ) || ! isset( $cache_entry->timestamp ) || ! is_numeric( $cache_entry->timestamp ) ) { return false; } $this->set( $key, $cache_entry->result, $expiration, $cache_entry->created ); return true; } /** * Set cached item as expired. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @param string $key */ function expire( $key ) { $this->_logger->entrance( 'key = ' . $key ); $cache_entry = $this->_options->get_option( $key, false ); if ( is_object( $cache_entry ) && isset( $cache_entry->timestamp ) && is_numeric( $cache_entry->timestamp ) ) { // Set to expired. $cache_entry->timestamp = WP_FS__SCRIPT_START_TIME; $this->_options->set_option( $key, $cache_entry, true ); } } #-------------------------------------------------------------------------------- #region Migration #-------------------------------------------------------------------------------- /** * Migrate options from site level. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function migrate_to_network() { $this->_options->migrate_to_network(); } #endregion }freemius/includes/managers/class-fs-admin-notice-manager.php000064400000044100147600046700020172 0ustar00 0 ) { $key .= ":{$network_level_or_blog_id}"; } else { $network_level_or_blog_id = get_current_blog_id(); $key .= ":{$network_level_or_blog_id}"; } } if ( ! isset( self::$_instances[ $key ] ) ) { self::$_instances[ $key ] = new FS_Admin_Notice_Manager( $id, $title, $module_unique_affix, $is_network_and_blog_admins, $network_level_or_blog_id ); } return self::$_instances[ $key ]; } /** * @param string $id * @param string $title * @param string $module_unique_affix * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and * blog admin pages. * @param bool|int $network_level_or_blog_id */ protected function __construct( $id, $title = '', $module_unique_affix = '', $is_network_and_blog_admins = false, $network_level_or_blog_id = false ) { $this->_id = $id; $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $this->_id . '_data', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); $this->_title = ! empty( $title ) ? $title : ''; $this->_module_unique_affix = $module_unique_affix; $this->_sticky_storage = FS_Key_Value_Storage::instance( 'admin_notices', $this->_id, $network_level_or_blog_id ); if ( is_multisite() ) { $this->_is_network_notices = ( true === $network_level_or_blog_id ); if ( is_numeric( $network_level_or_blog_id ) ) { $this->_blog_id = $network_level_or_blog_id; } } else { $this->_is_network_notices = false; } $is_network_admin = fs_is_network_admin(); $is_blog_admin = fs_is_blog_admin(); if ( ( $this->_is_network_notices && $is_network_admin ) || ( ! $this->_is_network_notices && $is_blog_admin ) || ( $is_network_and_blog_admins && ( $is_network_admin || $is_blog_admin ) ) ) { if ( 0 < count( $this->_sticky_storage ) ) { $ajax_action_suffix = str_replace( ':', '-', $this->_id ); // If there are sticky notices for the current slug, add a callback // to the AJAX action that handles message dismiss. add_action( "wp_ajax_fs_dismiss_notice_action_{$ajax_action_suffix}", array( &$this, 'dismiss_notice_ajax_callback' ) ); foreach ( $this->_sticky_storage as $msg ) { // Add admin notice. $this->add( $msg['message'], $msg['title'], $msg['type'], true, $msg['id'], false, isset( $msg['wp_user_id'] ) ? $msg['wp_user_id'] : null, ! empty( $msg['plugin'] ) ? $msg['plugin'] : null, $is_network_and_blog_admins, isset( $msg['dismissible'] ) ? $msg['dismissible'] : null ); } } } } /** * Remove sticky message by ID. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * */ function dismiss_notice_ajax_callback() { check_admin_referer( 'fs_dismiss_notice_action' ); if ( ! is_numeric( $_POST['message_id'] ) ) { $this->_sticky_storage->remove( $_POST['message_id'] ); } wp_die(); } /** * Rendered sticky message dismiss JavaScript. * * @author Vova Feldman (@svovaf) * @since 1.0.7 */ static function _add_sticky_dismiss_javascript() { $params = array(); fs_require_once_template( 'sticky-admin-notice-js.php', $params ); } private static $_added_sticky_javascript = false; /** * Hook to the admin_footer to add sticky message dismiss JavaScript handler. * * @author Vova Feldman (@svovaf) * @since 1.0.7 */ private static function has_sticky_messages() { if ( ! self::$_added_sticky_javascript ) { add_action( 'admin_footer', array( 'FS_Admin_Notice_Manager', '_add_sticky_dismiss_javascript' ) ); } } /** * Handle admin_notices by printing the admin messages stacked in the queue. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * */ function _admin_notices_hook() { if ( function_exists( 'current_user_can' ) && ! current_user_can( 'manage_options' ) ) { // Only show messages to admins. return; } foreach ( $this->_notices as $id => $msg ) { if ( isset( $msg['wp_user_id'] ) && is_numeric( $msg['wp_user_id'] ) ) { if ( get_current_user_id() != $msg['wp_user_id'] ) { continue; } } /** * Added a filter to control the visibility of admin notices. * * Usage example: * * /** * * @param bool $show * * @param array $msg { * * @var string $message The actual message. * * @var string $title An optional message title. * * @var string $type The type of the message ('success', 'update', 'warning', 'promotion'). * * @var string $id The unique identifier of the message. * * @var string $manager_id The unique identifier of the notices manager. For plugins it would be the plugin's slug, for themes - `-theme`. * * @var string $plugin The product's title. * * @var string $wp_user_id An optional WP user ID that this admin notice is for. * * } * * * * @return bool * *\/ * function my_custom_show_admin_notice( $show, $msg ) { * if ('trial_promotion' != $msg['id']) { * return false; * } * * return $show; * } * * my_fs()->add_filter( 'show_admin_notice', 'my_custom_show_admin_notice', 10, 2 ); * * @author Vova Feldman * @since 2.2.0 */ $show_notice = call_user_func_array( 'fs_apply_filter', array( $this->_module_unique_affix, 'show_admin_notice', $this->show_admin_notices(), $msg ) ); if ( true !== $show_notice ) { continue; } fs_require_template( 'admin-notice.php', $msg ); if ( $msg['sticky'] ) { self::has_sticky_messages(); } } } /** * Enqueue common stylesheet to style admin notice. * * @author Vova Feldman (@svovaf) * @since 1.0.7 */ function _enqueue_styles() { fs_enqueue_local_style( 'fs_common', '/admin/common.css' ); } /** * Check if the current page is the Gutenberg block editor. * * @author Vova Feldman (@svovaf) * @since 2.2.3 * * @return bool */ function is_gutenberg_page() { if ( function_exists( 'is_gutenberg_page' ) && is_gutenberg_page() ) { // The Gutenberg plugin is on. return true; } $current_screen = get_current_screen(); if ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) { // Gutenberg page on 5+. return true; } return false; } /** * Check if admin notices should be shown on page. E.g., we don't want to show notices in the Visual Editor. * * @author Xiaheng Chen (@xhchen) * @since 2.4.2 * * @return bool */ function show_admin_notices() { global $pagenow; if ( 'about.php' === $pagenow ) { // Don't show admin notices on the About page. return false; } if ( $this->is_gutenberg_page() ) { // Don't show admin notices in Gutenberg (visual editor). return false; } return true; } /** * Add admin message to admin messages queue, and hook to admin_notices / all_admin_notices if not yet hooked. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param string $message * @param string $title * @param string $type * @param bool $is_sticky * @param string $id Message ID * @param bool $store_if_sticky * @param number|null $wp_user_id * @param string|null $plugin_title * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network * and blog admin pages. * @param bool|null $is_dismissible * @param array $data * * @uses add_action() */ function add( $message, $title = '', $type = 'success', $is_sticky = false, $id = '', $store_if_sticky = true, $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false, $is_dismissible = null, $data = array() ) { $notices_type = $this->get_notices_type(); if ( empty( $this->_notices ) ) { if ( ! $is_network_and_blog_admins ) { add_action( $notices_type, array( &$this, "_admin_notices_hook" ) ); } else { add_action( 'network_admin_notices', array( &$this, "_admin_notices_hook" ) ); add_action( 'admin_notices', array( &$this, "_admin_notices_hook" ) ); } add_action( 'admin_enqueue_scripts', array( &$this, '_enqueue_styles' ) ); } if ( '' === $id ) { $id = md5( $title . ' ' . $message . ' ' . $type ); } $message_object = array( 'message' => $message, 'title' => $title, 'type' => $type, 'sticky' => $is_sticky, 'id' => $id, 'manager_id' => $this->_id, 'plugin' => ( ! is_null( $plugin_title ) ? $plugin_title : $this->_title ), 'wp_user_id' => $wp_user_id, 'dismissible' => $is_dismissible, 'data' => $data ); if ( $is_sticky && $store_if_sticky ) { $this->_sticky_storage->{$id} = $message_object; } $this->_notices[ $id ] = $message_object; } /** * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param string|string[] $ids * @param bool $store */ function remove_sticky( $ids, $store = true ) { if ( ! is_array( $ids ) ) { $ids = array( $ids ); } foreach ( $ids as $id ) { // Remove from sticky storage. $this->_sticky_storage->remove( $id, $store ); if ( isset( $this->_notices[ $id ] ) ) { unset( $this->_notices[ $id ] ); } } } /** * Check if sticky message exists by id. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param $id * * @return bool */ function has_sticky( $id ) { return isset( $this->_sticky_storage[ $id ] ); } /** * Adds sticky admin notification. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param string $message * @param string $id Message ID * @param string $title * @param string $type * @param number|null $wp_user_id * @param string|null $plugin_title * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network * and blog admin pages. * @param bool $is_dimissible * @param array $data */ function add_sticky( $message, $id, $title = '', $type = 'success', $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false, $is_dimissible = true, $data = array() ) { if ( ! empty( $this->_module_unique_affix ) ) { $message = fs_apply_filter( $this->_module_unique_affix, "sticky_message_{$id}", $message ); $title = fs_apply_filter( $this->_module_unique_affix, "sticky_title_{$id}", $title ); } $this->add( $message, $title, $type, true, $id, true, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dimissible, $data ); } /** * Retrieves the data of an sticky notice. * * @author Leo Fajardo (@leorw) * @since 2.4.3 * * @param string $id Message ID. * * @return array|null */ function get_sticky( $id ) { return isset( $this->_sticky_storage->{$id} ) ? $this->_sticky_storage->{$id} : null; } /** * Clear all sticky messages. * * @author Vova Feldman (@svovaf) * @since 1.0.8 * * @param bool $is_temporary @since 2.5.1 */ function clear_all_sticky( $is_temporary = false ) { if ( $is_temporary ) { $this->_notices = array(); } else { $this->_sticky_storage->clear_all(); } } #-------------------------------------------------------------------------------- #region Helper Method #-------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return string */ private function get_notices_type() { return $this->_is_network_notices ? 'network_admin_notices' : 'admin_notices'; } #endregion } freemius/includes/managers/class-fs-admin-menu-manager.php000064400000063232147600046700017664 0ustar00 */ private $_default_submenu_items; /** * @since 1.1.3 * * @var string */ private $_first_time_path; /** * @since 1.2.2 * * @var bool */ private $_menu_exists; /** * @since 2.0.0 * * @var bool */ private $_network_menu_exists; #endregion Properties /** * @var FS_Logger */ protected $_logger; #region Singleton /** * @var FS_Admin_Menu_Manager[] */ private static $_instances = array(); /** * @param number $module_id * @param string $module_type * @param string $module_unique_affix * * @return FS_Admin_Menu_Manager */ static function instance( $module_id, $module_type, $module_unique_affix ) { $key = 'm_' . $module_id; if ( ! isset( self::$_instances[ $key ] ) ) { self::$_instances[ $key ] = new FS_Admin_Menu_Manager( $module_id, $module_type, $module_unique_affix ); } return self::$_instances[ $key ]; } protected function __construct( $module_id, $module_type, $module_unique_affix ) { $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $module_id . '_admin_menu', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); $this->_module_id = $module_id; $this->_module_type = $module_type; $this->_module_unique_affix = $module_unique_affix; } #endregion Singleton #region Helpers private function get_option( &$options, $key, $default = false ) { return ! empty( $options[ $key ] ) ? $options[ $key ] : $default; } private function get_bool_option( &$options, $key, $default = false ) { return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default; } #endregion Helpers /** * @param array $menu * @param bool $is_addon */ function init( $menu, $is_addon = false ) { $this->_menu_exists = ( isset( $menu['slug'] ) && ! empty( $menu['slug'] ) ); $this->_network_menu_exists = ( ! empty( $menu['network'] ) && true === $menu['network'] ); $this->_menu_slug = ( $this->_menu_exists ? $menu['slug'] : $this->_module_unique_affix ); $this->_default_submenu_items = array(); // @deprecated $this->_type = 'page'; $this->_is_top_level = true; $this->_is_override_exact = false; $this->_parent_slug = false; // @deprecated $this->_parent_type = 'page'; if ( isset( $menu ) ) { if ( ! $is_addon ) { $this->_default_submenu_items = array( 'contact' => $this->get_bool_option( $menu, 'contact', true ), 'support' => $this->get_bool_option( $menu, 'support', true ), 'affiliation' => $this->get_bool_option( $menu, 'affiliation', true ), 'account' => $this->get_bool_option( $menu, 'account', true ), 'pricing' => $this->get_bool_option( $menu, 'pricing', true ), 'addons' => $this->get_bool_option( $menu, 'addons', true ), ); // @deprecated $this->_type = $this->get_option( $menu, 'type', 'page' ); } $this->_is_override_exact = $this->get_bool_option( $menu, 'override_exact' ); if ( isset( $menu['parent'] ) ) { $this->_parent_slug = $this->get_option( $menu['parent'], 'slug' ); // @deprecated $this->_parent_type = $this->get_option( $menu['parent'], 'type', 'page' ); // If parent's slug is different, then it's NOT a top level menu item. $this->_is_top_level = ( $this->_parent_slug === $this->_menu_slug ); } else { /** * If no parent then top level if: * - Has custom admin menu ('page') * - CPT menu type ('cpt') */ // $this->_is_top_level = in_array( $this->_type, array( // 'cpt', // 'page' // ) ); } $first_path = $this->get_option( $menu, 'first-path', false ); if ( ! empty( $first_path ) && is_string( $first_path ) ) { $this->_first_time_path = $first_path; } } } /** * Check if top level menu. * * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return bool False if submenu item. */ function is_top_level() { return $this->_is_top_level; } /** * Check if the page should be override on exact URL match. * * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return bool False if submenu item. */ function is_override_exact() { return $this->_is_override_exact; } /** * Get the path of the page the user should be forwarded to after first activation. * * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param bool $is_network Since 2.4.5 * * @return string */ function get_first_time_path( $is_network = false ) { if ( empty ( $this->_first_time_path ) ) { return $this->_first_time_path; } if ( $is_network ) { return network_admin_url( $this->_first_time_path ); } else { return admin_url( $this->_first_time_path ); } } /** * Check if plugin's menu item is part of a custom top level menu. * * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return bool */ function has_custom_parent() { return ! $this->_is_top_level && is_string( $this->_parent_slug ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return bool */ function has_menu() { return $this->_menu_exists; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return bool */ function has_network_menu() { return $this->_network_menu_exists; } /** * @author Leo Fajardo (@leorw) * * @param string $menu_slug * * @since 2.1.3 */ function set_slug_and_network_menu_exists_flag($menu_slug ) { $this->_menu_slug = $menu_slug; $this->_network_menu_exists = false; } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param string $id * @param bool $default * @param bool $ignore_menu_existence Since 1.2.2.7 If true, check if the submenu item visible even if there's no parent menu. * * @return bool */ function is_submenu_item_visible( $id, $default = true, $ignore_menu_existence = false ) { if ( ! $ignore_menu_existence && ! $this->has_menu() ) { return false; } return fs_apply_filter( $this->_module_unique_affix, 'is_submenu_visible', $this->get_bool_option( $this->_default_submenu_items, $id, $default ), $id ); } /** * Calculates admin settings menu slug. * If plugin's menu slug is a file (e.g. CPT), uses plugin's slug as the menu slug. * * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param string $page * * @return string */ function get_slug( $page = '' ) { return ( ( false === strpos( $this->_menu_slug, '.php?' ) ) ? $this->_menu_slug : $this->_module_unique_affix ) . ( empty( $page ) ? '' : ( '-' . $page ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return string */ function get_parent_slug() { return $this->_parent_slug; } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return string */ function get_type() { return $this->_type; } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return bool */ function is_cpt() { return ( 0 === strpos( $this->_menu_slug, 'edit.php?post_type=' ) || // Back compatibility. 'cpt' === $this->_type ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return string */ function get_parent_type() { return $this->_parent_type; } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return string */ function get_raw_slug() { return $this->_menu_slug; } /** * Get plugin's original menu slug. * * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return string */ function get_original_menu_slug() { if ( 'cpt' === $this->_type ) { return add_query_arg( array( 'post_type' => $this->_menu_slug ), 'edit.php' ); } if ( false === strpos( $this->_menu_slug, '.php?' ) ) { return $this->_menu_slug; } else { return $this->_module_unique_affix; } } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @return string */ function get_top_level_menu_slug() { return $this->has_custom_parent() ? $this->get_parent_slug() : $this->get_raw_slug(); } /** * Is user on plugin's admin activation page. * * @author Vova Feldman (@svovaf) * @since 1.0.8 * * @param bool $show_opt_in_on_themes_page Since 2.3.1 * * @return bool * * @deprecated Please use is_activation_page() instead. */ function is_main_settings_page( $show_opt_in_on_themes_page = false ) { return $this->is_activation_page( $show_opt_in_on_themes_page ); } /** * Is user on product's admin activation page. * * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @param bool $show_opt_in_on_themes_page Since 2.3.1 * * @return bool */ function is_activation_page( $show_opt_in_on_themes_page = false ) { if ( $show_opt_in_on_themes_page ) { /** * In activation only when show_optin query string param is given. * * @since 1.2.2 */ return ( ( WP_FS__MODULE_TYPE_THEME === $this->_module_type ) && Freemius::is_themes_page() && fs_request_get_bool( $this->_module_unique_affix . '_show_optin' ) ); } if ( $this->_menu_exists && ( fs_is_plugin_page( $this->_menu_slug ) || fs_is_plugin_page( $this->_module_unique_affix ) ) ) { /** * Module has a settings menu and the context page is the main settings page, so assume it's in * activation (doesn't really check if already opted-in/skipped or not). * * @since 1.2.2 */ return true; } return false; } #region Submenu Override /** * Override submenu's action. * * @author Vova Feldman (@svovaf) * @since 1.1.0 * * @param string $parent_slug * @param string $menu_slug * @param callable $function * * @return false|string If submenu exist, will return the hook name. */ function override_submenu_action( $parent_slug, $menu_slug, $function ) { global $submenu; $menu_slug = plugin_basename( $menu_slug ); $parent_slug = plugin_basename( $parent_slug ); if ( ! isset( $submenu[ $parent_slug ] ) ) { // Parent menu not exist. return false; } $found_submenu_item = false; foreach ( $submenu[ $parent_slug ] as $submenu_item ) { if ( $menu_slug === $submenu_item[2] ) { $found_submenu_item = $submenu_item; break; } } if ( false === $found_submenu_item ) { // Submenu item not found. return false; } // Remove current function. $hookname = get_plugin_page_hookname( $menu_slug, $parent_slug ); remove_all_actions( $hookname ); // Attach new action. add_action( $hookname, $function ); return $hookname; } #endregion Submenu Override #region Top level menu Override /** * Find plugin's admin dashboard main menu item. * * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @return string[]|false */ private function find_top_level_menu() { global $menu; $position = - 1; $found_menu = false; $menu_slug = $this->get_raw_slug(); $hook_name = get_plugin_page_hookname( $menu_slug, '' ); foreach ( $menu as $pos => $m ) { if ( $menu_slug === $m[2] ) { $position = $pos; $found_menu = $m; break; } } if ( false === $found_menu ) { return false; } return array( 'menu' => $found_menu, 'position' => $position, 'hook_name' => $hook_name ); } /** * Find plugin's admin dashboard main submenu item. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @return array|false */ private function find_main_submenu() { global $submenu; $top_level_menu_slug = $this->get_top_level_menu_slug(); if ( ! isset( $submenu[ $top_level_menu_slug ] ) ) { return false; } $submenu_slug = $this->get_raw_slug(); $position = - 1; $found_submenu = false; $hook_name = get_plugin_page_hookname( $submenu_slug, '' ); foreach ( $submenu[ $top_level_menu_slug ] as $pos => $sub ) { if ( $submenu_slug === $sub[2] ) { $position = $pos; $found_submenu = $sub; } } if ( false === $found_submenu ) { return false; } return array( 'menu' => $found_submenu, 'parent_slug' => $top_level_menu_slug, 'position' => $position, 'hook_name' => $hook_name ); } /** * Remove all sub-menu items. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return bool If submenu with plugin's menu slug was found. */ private function remove_all_submenu_items() { global $submenu; $menu_slug = $this->get_raw_slug(); if ( ! isset( $submenu[ $menu_slug ] ) ) { return false; } /** * This method is NOT executed for WordPress.org themes. * Since we maintain only one version of the SDK we added this small * hack to avoid the error from Theme Check since it's a false-positive. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ $submenu_ref = &$submenu; $submenu_ref[ $menu_slug ] = array(); return true; } /** * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param bool $remove_top_level_menu * * @return false|array[string]mixed */ function remove_menu_item( $remove_top_level_menu = false ) { $this->_logger->entrance(); // Find main menu item. $top_level_menu = $this->find_top_level_menu(); if ( false === $top_level_menu ) { return false; } // Remove it with its actions. remove_all_actions( $top_level_menu['hook_name'] ); // Remove all submenu items. $this->remove_all_submenu_items(); if ( $remove_top_level_menu ) { global $menu; unset( $menu[ $top_level_menu['position'] ] ); } return $top_level_menu; } /** * Get module's main admin setting page URL. * * @todo This method was only tested for wp.org compliant themes with a submenu item. Need to test for plugins with top level, submenu, and CPT top level, menu items. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return string */ function main_menu_url() { $this->_logger->entrance(); if ( $this->_is_top_level ) { $menu = $this->find_top_level_menu(); } else { $menu = $this->find_main_submenu(); } $parent_slug = isset( $menu['parent_slug'] ) ? $menu['parent_slug'] : 'admin.php'; return admin_url( $parent_slug . ( false === strpos( $parent_slug, '?' ) ? '?' : '&' ) . 'page=' . $menu['menu'][2] ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.4 * * @param callable $function * * @return false|array[string]mixed */ function override_menu_item( $function ) { $found_menu = $this->remove_menu_item(); if ( false === $found_menu ) { return false; } if ( ! $this->is_top_level() || ! $this->is_cpt() ) { $menu_slug = plugin_basename( $this->get_slug() ); $hookname = get_plugin_page_hookname( $menu_slug, '' ); // Override menu action. add_action( $hookname, $function ); } else { global $menu; // Remove original CPT menu. unset( $menu[ $found_menu['position'] ] ); // Create new top-level menu action. $hookname = self::add_page( $found_menu['menu'][3], $found_menu['menu'][0], 'manage_options', $this->get_slug(), $function, $found_menu['menu'][6], $found_menu['position'] ); } return $hookname; } /** * Adds a counter to the module's top level menu item. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param int $counter * @param string $class */ function add_counter_to_menu_item( $counter = 1, $class = '' ) { global $menu, $submenu; $mask = '%s '; /** * This method is NOT executed for WordPress.org themes. * Since we maintain only one version of the SDK we added this small * hack to avoid the error from Theme Check since it's a false-positive. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ $menu_ref = &$menu; $submenu_ref = &$submenu; if ( $this->_is_top_level ) { // Find main menu item. $found_menu = $this->find_top_level_menu(); if ( false !== $found_menu ) { // Override menu label. $menu_ref[ $found_menu['position'] ][0] = sprintf( $mask, $found_menu['menu'][0], $class, $counter ); } } else { $found_submenu = $this->find_main_submenu(); if ( false !== $found_submenu ) { // Override menu label. $submenu_ref[ $found_submenu['parent_slug'] ][ $found_submenu['position'] ][0] = sprintf( $mask, $found_submenu['menu'][0], $class, $counter ); } } } #endregion Top level menu Override /** * Add a top-level menu page. * * Note for WordPress.org Theme/Plugin reviewer: * * This is a replication of `add_menu_page()` to avoid Theme Check warning. * * Why? * ==== * Freemius is an SDK for plugin and theme developers. Since the core * of the SDK is relevant both for plugins and themes, for obvious reasons, * we only develop and maintain one code base. * * This method will not run for wp.org themes (only plugins) since theme * admin settings/options are now only allowed in the customizer. * * If you have any questions or need clarifications, please don't hesitate * pinging me on slack, my username is @svovaf. * * @author Vova Feldman (@svovaf) * @since 1.2.2 * * @param string $page_title The text to be displayed in the title tags of the page when the menu is * selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable|string $function The function to be called to output the content for this page. * @param string $icon_url The URL to the icon to be used for this menu. * * Pass a base64-encoded SVG using a data URI, which will be colored to * match the color scheme. This should begin with * 'data:image/svg+xml;base64,'. * * Pass the name of a Dashicons helper class to use a font icon, * e.g. 'dashicons-chart-pie'. * * Pass 'none' to leave div.wp-menu-image empty so an icon can be added * via CSS. * @param int $position The position in the menu order this one should appear. * * @return string The resulting page's hook_suffix. */ static function add_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) { $fn = 'add_menu' . '_page'; return $fn( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position ); } /** * Add page and update menu instance settings. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $page_title * @param string $menu_title * @param string $capability * @param string $menu_slug * @param callable|string $function * @param string $icon_url * @param int|null $position * * @return string */ function add_page_and_update( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) { $this->_menu_slug = $menu_slug; $this->_is_top_level = true; $this->_menu_exists = true; $this->_network_menu_exists = true; return self::add_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position ); } /** * Add a submenu page. * * Note for WordPress.org Theme/Plugin reviewer: * * This is a replication of `add_submenu_page()` to avoid Theme Check warning. * * Why? * ==== * Freemius is an SDK for plugin and theme developers. Since the core * of the SDK is relevant both for plugins and themes, for obvious reasons, * we only develop and maintain one code base. * * This method will not run for wp.org themes (only plugins) since theme * admin settings/options are now only allowed in the customizer. * * If you have any questions or need clarifications, please don't hesitate * pinging me on slack, my username is @svovaf. * * @author Vova Feldman (@svovaf) * @since 1.2.2 * * @param string $parent_slug The slug name for the parent menu (or the file name of a standard * WordPress admin page). * @param string $page_title The text to be displayed in the title tags of the page when the menu is * selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable|string $function The function to be called to output the content for this page. * * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability * required. */ static function add_subpage( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { $fn = 'add_submenu' . '_page'; return $fn( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function ); } /** * Add sub page and update menu instance settings. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $parent_slug * @param string $page_title * @param string $menu_title * @param string $capability * @param string $menu_slug * @param callable|string $function * * @return string */ function add_subpage_and_update( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { $this->_menu_slug = $menu_slug; $this->_parent_slug = $parent_slug; $this->_is_top_level = false; $this->_menu_exists = true; $this->_network_menu_exists = true; return self::add_subpage( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function ); } }freemius/includes/sdk/Exceptions/OAuthException.php000064400000000515147600046700016510 0ustar00_result = $result; $code = 0; $message = 'Unknown error, please check GetResult().'; $type = ''; if ( isset( $result['error'] ) && is_array( $result['error'] ) ) { if ( isset( $result['error']['code'] ) ) { $code = $result['error']['code']; } if ( isset( $result['error']['message'] ) ) { $message = $result['error']['message']; } if ( isset( $result['error']['type'] ) ) { $type = $result['error']['type']; } } $this->_type = $type; $this->_code = $code; parent::__construct( $message, is_numeric( $code ) ? $code : 0 ); } /** * Return the associated result object returned by the API server. * * @return array The result from the API server */ public function getResult() { return $this->_result; } public function getStringCode() { return $this->_code; } public function getType() { return $this->_type; } /** * To make debugging easier. * * @return string The string representation of the error */ public function __toString() { $str = $this->getType() . ': '; if ( $this->code != 0 ) { $str .= $this->getStringCode() . ': '; } return $str . $this->getMessage(); } } } freemius/includes/sdk/Exceptions/EmptyArgumentException.php000064400000000444147600046700020272 0ustar00 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. {signature of Ty Coon}, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. freemius/includes/sdk/index.php000064400000000127147600046700012576 0ustar00 '7.37' ); if ( ! defined( 'FS_API__PROTOCOL' ) ) { define( 'FS_API__PROTOCOL', version_compare( $curl_version['version'], '7.37', '>=' ) ? 'https' : 'http' ); } if ( ! defined( 'FS_API__LOGGER_ON' ) ) { define( 'FS_API__LOGGER_ON', false ); } if ( ! defined( 'FS_API__ADDRESS' ) ) { define( 'FS_API__ADDRESS', '://api.freemius.com' ); } if ( ! defined( 'FS_API__SANDBOX_ADDRESS' ) ) { define( 'FS_API__SANDBOX_ADDRESS', '://sandbox-api.freemius.com' ); } if ( ! class_exists( 'Freemius_Api_WordPress' ) ) { class Freemius_Api_WordPress extends Freemius_Api_Base { private static $_logger = array(); /** * @param string $pScope 'app', 'developer', 'user' or 'install'. * @param number $pID Element's id. * @param string $pPublic Public key. * @param string|bool $pSecret Element's secret key. * @param bool $pSandbox Whether or not to run API in sandbox mode. */ public function __construct( $pScope, $pID, $pPublic, $pSecret = false, $pSandbox = false ) { // If secret key not provided, use public key encryption. if ( is_bool( $pSecret ) ) { $pSecret = $pPublic; } parent::Init( $pScope, $pID, $pPublic, $pSecret, $pSandbox ); } public static function GetUrl( $pCanonizedPath = '', $pIsSandbox = false ) { $address = ( $pIsSandbox ? FS_API__SANDBOX_ADDRESS : FS_API__ADDRESS ); if ( ':' === $address[0] ) { $address = self::$_protocol . $address; } return $address . $pCanonizedPath; } #---------------------------------------------------------------------------------- #region Servers Clock Diff #---------------------------------------------------------------------------------- /** * @var int Clock diff in seconds between current server to API server. */ private static $_clock_diff = 0; /** * Set clock diff for all API calls. * * @since 1.0.3 * * @param $pSeconds */ public static function SetClockDiff( $pSeconds ) { self::$_clock_diff = $pSeconds; } /** * Find clock diff between current server to API server. * * @since 1.0.2 * @return int Clock diff in seconds. */ public static function FindClockDiff() { $time = time(); $pong = self::Ping(); return ( $time - strtotime( $pong->timestamp ) ); } #endregion /** * @var string http or https */ private static $_protocol = FS_API__PROTOCOL; /** * Set API connection protocol. * * @since 1.0.4 */ public static function SetHttp() { self::$_protocol = 'http'; } /** * Sets API connection protocol to HTTPS. * * @since 2.5.4 */ public static function SetHttps() { self::$_protocol = 'https'; } /** * @since 1.0.4 * * @return bool */ public static function IsHttps() { return ( 'https' === self::$_protocol ); } /** * Sign request with the following HTTP headers: * Content-MD5: MD5(HTTP Request body) * Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000) * Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign, * {scope_entity_secret_key})) * * @param string $pResourceUrl * @param array $pWPRemoteArgs * * @return array */ function SignRequest( $pResourceUrl, $pWPRemoteArgs ) { $auth = $this->GenerateAuthorizationParams( $pResourceUrl, $pWPRemoteArgs['method'], ! empty( $pWPRemoteArgs['body'] ) ? $pWPRemoteArgs['body'] : '' ); $pWPRemoteArgs['headers']['Date'] = $auth['date']; $pWPRemoteArgs['headers']['Authorization'] = $auth['authorization']; if ( ! empty( $auth['content_md5'] ) ) { $pWPRemoteArgs['headers']['Content-MD5'] = $auth['content_md5']; } return $pWPRemoteArgs; } /** * Generate Authorization request headers: * * Content-MD5: MD5(HTTP Request body) * Date: Current date (i.e Sat, 14 Feb 2016 20:24:46 +0000) * Authorization: FS {scope_entity_id}:{scope_entity_public_key}:base64encode(sha256(string_to_sign, * {scope_entity_secret_key})) * * @author Vova Feldman * * @param string $pResourceUrl * @param string $pMethod * @param string $pPostParams * * @return array * @throws Freemius_Exception */ function GenerateAuthorizationParams( $pResourceUrl, $pMethod = 'GET', $pPostParams = '' ) { $pMethod = strtoupper( $pMethod ); $eol = "\n"; $content_md5 = ''; $content_type = ''; $now = ( time() - self::$_clock_diff ); $date = date( 'r', $now ); if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) { $content_type = 'application/json'; if ( ! empty( $pPostParams ) ) { $content_md5 = md5( $pPostParams ); } } $string_to_sign = implode( $eol, array( $pMethod, $content_md5, $content_type, $date, $pResourceUrl ) ); // If secret and public keys are identical, it means that // the signature uses public key hash encoding. $auth_type = ( $this->_secret !== $this->_public ) ? 'FS' : 'FSP'; $auth = array( 'date' => $date, 'authorization' => $auth_type . ' ' . $this->_id . ':' . $this->_public . ':' . self::Base64UrlEncode( hash_hmac( 'sha256', $string_to_sign, $this->_secret ) ) ); if ( ! empty( $content_md5 ) ) { $auth['content_md5'] = $content_md5; } return $auth; } /** * Get API request URL signed via query string. * * @since 1.2.3 Stopped using http_build_query(). Instead, use urlencode(). In some environments the encoding of http_build_query() can generate a URL that once used with a redirect, the `&` querystring separator is escaped to `&` which breaks the URL (Added by @svovaf). * * @param string $pPath * * @throws Freemius_Exception * * @return string */ function GetSignedUrl( $pPath ) { $resource = explode( '?', $this->CanonizePath( $pPath ) ); $pResourceUrl = $resource[0]; $auth = $this->GenerateAuthorizationParams( $pResourceUrl ); return Freemius_Api_WordPress::GetUrl( $pResourceUrl . '?' . ( 1 < count( $resource ) && ! empty( $resource[1] ) ? $resource[1] . '&' : '' ) . 'authorization=' . urlencode( $auth['authorization'] ) . '&auth_date=' . urlencode( $auth['date'] ) , $this->_isSandbox ); } /** * @author Vova Feldman * * @param string $pUrl * @param array $pWPRemoteArgs * * @return mixed */ private static function ExecuteRequest( $pUrl, &$pWPRemoteArgs ) { $bt = debug_backtrace(); $start = microtime( true ); $response = self::RemoteRequest( $pUrl, $pWPRemoteArgs ); if ( FS_API__LOGGER_ON ) { $end = microtime( true ); $has_body = ( isset( $pWPRemoteArgs['body'] ) && ! empty( $pWPRemoteArgs['body'] ) ); $is_http_error = is_wp_error( $response ); self::$_logger[] = array( 'id' => count( self::$_logger ), 'start' => $start, 'end' => $end, 'total' => ( $end - $start ), 'method' => $pWPRemoteArgs['method'], 'path' => $pUrl, 'body' => $has_body ? $pWPRemoteArgs['body'] : null, 'result' => ! $is_http_error ? $response['body'] : json_encode( $response->get_error_messages() ), 'code' => ! $is_http_error ? $response['response']['code'] : null, 'backtrace' => $bt, ); } return $response; } /** * @author Leo Fajardo (@leorw) * * @param string $pUrl * @param array $pWPRemoteArgs * * @return array|WP_Error The response array or a WP_Error on failure. */ static function RemoteRequest( $pUrl, $pWPRemoteArgs ) { $response = wp_remote_request( $pUrl, $pWPRemoteArgs ); if ( is_array( $response ) && ( empty( $response['headers'] ) || empty( $response['headers']['x-api-server'] ) ) ) { // API is considered blocked if the response doesn't include the `x-api-server` header. When there's no error but this header doesn't exist, the response is usually not in the expected form (e.g., cannot be JSON-decoded). $response = new WP_Error( 'api_blocked', htmlentities( $response['body'] ) ); } return $response; } /** * @return array */ static function GetLogger() { return self::$_logger; } /** * @param string $pCanonizedPath * @param string $pMethod * @param array $pParams * @param null|array $pWPRemoteArgs * @param bool $pIsSandbox * @param null|callable $pBeforeExecutionFunction * * @return object[]|object|null * * @throws \Freemius_Exception */ private static function MakeStaticRequest( $pCanonizedPath, $pMethod = 'GET', $pParams = array(), $pWPRemoteArgs = null, $pIsSandbox = false, $pBeforeExecutionFunction = null ) { // Connectivity errors simulation. if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_CLOUDFLARE ) { self::ThrowCloudFlareDDoSException(); } else if ( FS_SDK__SIMULATE_NO_API_CONNECTIVITY_SQUID_ACL ) { self::ThrowSquidAclException(); } if ( empty( $pWPRemoteArgs ) ) { $user_agent = 'Freemius/WordPress-SDK/' . Freemius_Api_Base::VERSION . '; ' . home_url(); $pWPRemoteArgs = array( 'method' => strtoupper( $pMethod ), 'connect_timeout' => 10, 'timeout' => 60, 'follow_redirects' => true, 'redirection' => 5, 'user-agent' => $user_agent, 'blocking' => true, ); } if ( ! isset( $pWPRemoteArgs['headers'] ) || ! is_array( $pWPRemoteArgs['headers'] ) ) { $pWPRemoteArgs['headers'] = array(); } if ( in_array( $pMethod, array( 'POST', 'PUT' ) ) ) { $pWPRemoteArgs['headers']['Content-type'] = 'application/json'; if ( is_array( $pParams ) && 0 < count( $pParams ) ) { $pWPRemoteArgs['body'] = json_encode( $pParams ); } } $request_url = self::GetUrl( $pCanonizedPath, $pIsSandbox ); $resource = explode( '?', $pCanonizedPath ); if ( FS_SDK__HAS_CURL ) { // Disable the 'Expect: 100-continue' behaviour. This causes cURL to wait // for 2 seconds if the server does not support this header. $pWPRemoteArgs['headers']['Expect'] = ''; } if ( 'https' === substr( strtolower( $request_url ), 0, 5 ) ) { $pWPRemoteArgs['sslverify'] = FS_SDK__SSLVERIFY; } if ( false !== $pBeforeExecutionFunction && is_callable( $pBeforeExecutionFunction ) ) { $pWPRemoteArgs = call_user_func( $pBeforeExecutionFunction, $resource[0], $pWPRemoteArgs ); } $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs ); if ( is_wp_error( $result ) ) { /** * @var WP_Error $result */ if ( self::IsCurlError( $result ) ) { /** * With dual stacked DNS responses, it's possible for a server to * have IPv6 enabled but not have IPv6 connectivity. If this is * the case, cURL will try IPv4 first and if that fails, then it will * fall back to IPv6 and the error EHOSTUNREACH is returned by the * operating system. */ $matches = array(); $regex = '/Failed to connect to ([^:].*): Network is unreachable/'; if ( preg_match( $regex, $result->get_error_message( 'http_request_failed' ), $matches ) ) { /** * Validate IP before calling `inet_pton()` to avoid PHP un-catchable warning. * @author Vova Feldman (@svovaf) */ if ( filter_var( $matches[1], FILTER_VALIDATE_IP ) ) { if ( strlen( inet_pton( $matches[1] ) ) === 16 ) { /** * error_log('Invalid IPv6 configuration on server, Please disable or get native IPv6 on your server.'); * Hook to an action triggered just before cURL is executed to resolve the IP version to v4. * * @phpstan-ignore-next-line */ add_action( 'http_api_curl', 'Freemius_Api_WordPress::CurlResolveToIPv4', 10, 1 ); // Re-run request. $result = self::ExecuteRequest( $request_url, $pWPRemoteArgs ); } } } } if ( is_wp_error( $result ) ) { self::ThrowWPRemoteException( $result ); } } $response_body = $result['body']; if ( empty( $response_body ) ) { return null; } $decoded = json_decode( $response_body ); if ( is_null( $decoded ) ) { if ( preg_match( '/Please turn JavaScript on/i', $response_body ) && preg_match( '/text\/javascript/', $response_body ) ) { self::ThrowCloudFlareDDoSException( $response_body ); } else if ( preg_match( '/Access control configuration prevents your request from being allowed at this time. Please contact your service provider if you feel this is incorrect./', $response_body ) && preg_match( '/squid/', $response_body ) ) { self::ThrowSquidAclException( $response_body ); } else { $decoded = (object) array( 'error' => (object) array( 'type' => 'Unknown', 'message' => $response_body, 'code' => 'unknown', 'http' => 402 ) ); } } return $decoded; } /** * Makes an HTTP request. This method can be overridden by subclasses if * developers want to do fancier things or use something other than wp_remote_request() * to make the request. * * @param string $pCanonizedPath The URL to make the request to * @param string $pMethod HTTP method * @param array $pParams The parameters to use for the POST body * @param null|array $pWPRemoteArgs wp_remote_request options. * * @return object[]|object|null * * @throws Freemius_Exception */ public function MakeRequest( $pCanonizedPath, $pMethod = 'GET', $pParams = array(), $pWPRemoteArgs = null ) { $resource = explode( '?', $pCanonizedPath ); // Only sign request if not ping.json connectivity test. $sign_request = ( '/v1/ping.json' !== strtolower( substr( $resource[0], - strlen( '/v1/ping.json' ) ) ) ); return self::MakeStaticRequest( $pCanonizedPath, $pMethod, $pParams, $pWPRemoteArgs, $this->_isSandbox, $sign_request ? array( &$this, 'SignRequest' ) : null ); } /** * Sets CURLOPT_IPRESOLVE to CURL_IPRESOLVE_V4 for cURL-Handle provided as parameter * * @param resource $handle A cURL handle returned by curl_init() * * @return resource $handle A cURL handle returned by curl_init() with CURLOPT_IPRESOLVE set to * CURL_IPRESOLVE_V4 * * @link https://gist.github.com/golderweb/3a2aaec2d56125cc004e */ static function CurlResolveToIPv4( $handle ) { curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 ); return $handle; } #---------------------------------------------------------------------------------- #region Connectivity Test #---------------------------------------------------------------------------------- /** * This method exists only for backward compatibility to prevent a fatal error from happening when called from an outdated piece of code. * * @param mixed $pPong * * @return bool */ public static function Test( $pPong = null ) { return ( is_object( $pPong ) && isset( $pPong->api ) && 'pong' === $pPong->api ); } /** * Ping API to test connectivity. * * @return object */ public static function Ping() { try { $result = self::MakeStaticRequest( '/v' . FS_API__VERSION . '/ping.json' ); } catch ( Freemius_Exception $e ) { // Map to error object. $result = (object) $e->getResult(); } catch ( Exception $e ) { // Map to error object. $result = (object) array( 'error' => (object) array( 'type' => 'Unknown', 'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')', 'code' => 'unknown', 'http' => 402 ) ); } return $result; } #endregion #---------------------------------------------------------------------------------- #region Connectivity Exceptions #---------------------------------------------------------------------------------- /** * @param \WP_Error $pError * * @return bool */ private static function IsCurlError( WP_Error $pError ) { $message = $pError->get_error_message( 'http_request_failed' ); return ( 0 === strpos( $message, 'cURL' ) ); } /** * @param WP_Error $pError * * @throws Freemius_Exception */ private static function ThrowWPRemoteException( WP_Error $pError ) { if ( self::IsCurlError( $pError ) ) { $message = $pError->get_error_message( 'http_request_failed' ); #region Check if there are any missing cURL methods. $curl_required_methods = array( 'curl_version', 'curl_exec', 'curl_init', 'curl_close', 'curl_setopt', 'curl_setopt_array', 'curl_error', ); // Find all missing methods. $missing_methods = array(); foreach ( $curl_required_methods as $m ) { if ( ! function_exists( $m ) ) { $missing_methods[] = $m; } } if ( ! empty( $missing_methods ) ) { throw new Freemius_Exception( array( 'error' => (object) array( 'type' => 'cUrlMissing', 'message' => $message, 'code' => 'curl_missing', 'http' => 402 ), 'missing_methods' => $missing_methods, ) ); } #endregion // cURL error - "cURL error {{errno}}: {{error}}". $parts = explode( ':', substr( $message, strlen( 'cURL error ' ) ), 2 ); $code = ( 0 < count( $parts ) ) ? $parts[0] : 'http_request_failed'; $message = ( 1 < count( $parts ) ) ? $parts[1] : $message; $e = new Freemius_Exception( array( 'error' => (object) array( 'code' => $code, 'message' => $message, 'type' => 'CurlException', ), ) ); } else { $e = new Freemius_Exception( array( 'error' => (object) array( 'code' => $pError->get_error_code(), 'message' => $pError->get_error_message(), 'type' => 'WPRemoteException', ), ) ); } throw $e; } /** * @param string $pResult * * @throws Freemius_Exception */ private static function ThrowCloudFlareDDoSException( $pResult = '' ) { throw new Freemius_Exception( array( 'error' => (object) array( 'type' => 'CloudFlareDDoSProtection', 'message' => $pResult, 'code' => 'cloudflare_ddos_protection', 'http' => 402 ) ) ); } /** * @param string $pResult * * @throws Freemius_Exception */ private static function ThrowSquidAclException( $pResult = '' ) { throw new Freemius_Exception( array( 'error' => (object) array( 'type' => 'SquidCacheBlock', 'message' => $pResult, 'code' => 'squid_cache_block', 'http' => 402 ) ) ); } #endregion } } freemius/includes/sdk/FreemiusBase.php000064400000017553147600046700014054 0ustar00_id = $pID; $this->_public = $pPublic; $this->_secret = $pSecret; $this->_scope = $pScope; $this->_isSandbox = $pIsSandbox; } public function IsSandbox() { return $this->_isSandbox; } function CanonizePath( $pPath ) { $pPath = trim( $pPath, '/' ); $query_pos = strpos( $pPath, '?' ); $query = ''; if ( false !== $query_pos ) { $query = substr( $pPath, $query_pos ); $pPath = substr( $pPath, 0, $query_pos ); } // Trim '.json' suffix. $format_length = strlen( '.' . self::FORMAT ); $start = $format_length * ( - 1 ); //negative if ( substr( strtolower( $pPath ), $start ) === ( '.' . self::FORMAT ) ) { $pPath = substr( $pPath, 0, strlen( $pPath ) - $format_length ); } switch ( $this->_scope ) { case 'app': $base = '/apps/' . $this->_id; break; case 'developer': $base = '/developers/' . $this->_id; break; case 'user': $base = '/users/' . $this->_id; break; case 'plugin': $base = '/plugins/' . $this->_id; break; case 'install': $base = '/installs/' . $this->_id; break; default: throw new Freemius_Exception( 'Scope not implemented.' ); } return '/v' . FS_API__VERSION . $base . ( ! empty( $pPath ) ? '/' : '' ) . $pPath . ( ( false === strpos( $pPath, '.' ) ) ? '.' . self::FORMAT : '' ) . $query; } abstract function MakeRequest( $pCanonizedPath, $pMethod = 'GET', $pParams = array() ); /** * @param string $pPath * @param string $pMethod * @param array $pParams * * @return object[]|object|null */ private function _Api( $pPath, $pMethod = 'GET', $pParams = array() ) { $pMethod = strtoupper( $pMethod ); try { $result = $this->MakeRequest( $pPath, $pMethod, $pParams ); } catch ( Freemius_Exception $e ) { // Map to error object. $result = (object) $e->getResult(); } catch ( Exception $e ) { // Map to error object. $result = (object) array( 'error' => (object) array( 'type' => 'Unknown', 'message' => $e->getMessage() . ' (' . $e->getFile() . ': ' . $e->getLine() . ')', 'code' => 'unknown', 'http' => 402 ) ); } return $result; } public function Api( $pPath, $pMethod = 'GET', $pParams = array() ) { return $this->_Api( $this->CanonizePath( $pPath ), $pMethod, $pParams ); } /** * Base64 decoding that does not need to be urldecode()-ed. * * Exactly the same as PHP base64 encode except it uses * `-` instead of `+` * `_` instead of `/` * No padded = * * @param string $input Base64UrlEncoded() string * * @return string */ protected static function Base64UrlDecode( $input ) { /** * IMPORTANT NOTE: * This is a hack suggested by @otto42 and @greenshady from * the theme's review team. The usage of base64 for API * signature encoding was approved in a Slack meeting * held on Tue (10/25 2016). * * @todo Remove this hack once the base64 error is removed from the Theme Check. * * @since 1.2.2 * @author Vova Feldman (@svovaf) */ $fn = 'base64' . '_decode'; return $fn( strtr( $input, '-_', '+/' ) ); } /** * Base64 encoding that does not need to be urlencode()ed. * * Exactly the same as base64 encode except it uses * `-` instead of `+ * `_` instead of `/` * * @param string $input string * * @return string Base64 encoded string */ protected static function Base64UrlEncode( $input ) { /** * IMPORTANT NOTE: * This is a hack suggested by @otto42 and @greenshady from * the theme's review team. The usage of base64 for API * signature encoding was approved in a Slack meeting * held on Tue (10/25 2016). * * @todo Remove this hack once the base64 error is removed from the Theme Check. * * @since 1.2.2 * @author Vova Feldman (@svovaf) */ $fn = 'base64' . '_encode'; $str = strtr( $fn( $input ), '+/', '-_' ); $str = str_replace( '=', '', $str ); return $str; } } }freemius/includes/supplements/index.php000064400000000127147600046700014374 0ustar00 $install ) { if ( true === $install->is_disconnected ) { $permission_manager->update_site_tracking( false, ( 0 == $blog_id ) ? null : $blog_id, // Update only if permissions are not yet set. true ); } } } }freemius/includes/supplements/fs-essential-functions-2.2.1.php000064400000002513147600046700020327 0ustar00 $data ) { if ( 0 === strpos( $file_real_path, fs_normalize_path( dirname( realpath( WP_PLUGIN_DIR . '/' . $relative_path ) ) . '/' ) ) ) { if ( '.' !== dirname( trailingslashit( $relative_path ) ) ) { return $relative_path; } } } return null; } freemius/includes/l10n.php000064400000002140147600046700011455 0ustar00_fs = $fs; $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $fs->get_slug() . '_info', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); // Remove default plugin information action. remove_all_actions( 'install_plugins_pre_plugin-information' ); // Override action with custom plugins function for add-ons. add_action( 'install_plugins_pre_plugin-information', array( &$this, 'install_plugin_information' ) ); // Override request for plugin information for Add-ons. add_filter( 'fs_plugins_api', array( &$this, '_get_addon_info_filter' ), WP_FS__DEFAULT_PRIORITY, 3 ); } /** * Generate add-on plugin information. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param array $data * @param string $action * @param object|null $args * * @return array|null */ function _get_addon_info_filter( $data, $action = '', $args = null ) { $this->_logger->entrance(); $parent_plugin_id = fs_request_get( 'parent_plugin_id', $this->_fs->get_id() ); if ( $this->_fs->get_id() != $parent_plugin_id || ( 'plugin_information' !== $action ) || ! isset( $args->slug ) ) { return $data; } // Find add-on by slug. $selected_addon = $this->_fs->get_addon_by_slug( $args->slug, WP_FS__DEV_MODE ); if ( false === $selected_addon ) { return $data; } if ( ! isset( $selected_addon->info ) ) { // Setup some default info. $selected_addon->info = new stdClass(); $selected_addon->info->selling_point_0 = 'Selling Point 1'; $selected_addon->info->selling_point_1 = 'Selling Point 2'; $selected_addon->info->selling_point_2 = 'Selling Point 3'; $selected_addon->info->description = '

    Tell your users all about your add-on

    '; } fs_enqueue_local_style( 'fs_addons', '/admin/add-ons.css' ); $data = $args; $has_free_plan = false; $has_paid_plan = false; // Load add-on pricing. $has_pricing = false; $has_features = false; $plans = false; $result = $this->_fs->get_api_plugin_scope()->get( $this->_fs->add_show_pending( "/addons/{$selected_addon->id}/pricing.json?type=visible" ) ); if ( ! isset( $result->error ) ) { $plans = $result->plans; if ( is_array( $plans ) ) { for ( $i = 0, $len = count( $plans ); $i < $len; $i ++ ) { $pricing = isset( $plans[ $i ]->pricing ) ? $plans[ $i ]->pricing : null; $features = isset( $plans[ $i ]->features ) ? $plans[ $i ]->features : null; $plans[ $i ] = new FS_Plugin_Plan( $plans[ $i ] ); $plan = $plans[ $i ]; if ( 'free' == $plans[ $i ]->name || ! is_array( $pricing ) || 0 == count( $pricing ) ) { $has_free_plan = true; } if ( is_array( $pricing ) && 0 < count( $pricing ) ) { $filtered_pricing = array(); foreach ( $pricing as $prices ) { $prices = new FS_Pricing( $prices ); if ( ! $prices->is_usd() ) { /** * Skip non-USD pricing. * * @author Leo Fajardo (@leorw) * @since 2.3.1 */ continue; } if ( ( $prices->has_monthly() && $prices->monthly_price > 1.0 ) || ( $prices->has_annual() && $prices->annual_price > 1.0 ) || ( $prices->has_lifetime() && $prices->lifetime_price > 1.0 ) ) { $filtered_pricing[] = $prices; } } if ( ! empty( $filtered_pricing ) ) { $has_paid_plan = true; $plan->pricing = $filtered_pricing; $has_pricing = true; } } if ( is_array( $features ) && 0 < count( $features ) ) { $plan->features = $features; $has_features = true; } } } } $latest = null; if ( ! $has_paid_plan && $selected_addon->is_wp_org_compliant ) { $repo_data = FS_Plugin_Updater::_fetch_plugin_info_from_repository( 'plugin_information', (object) array( 'slug' => $selected_addon->slug, 'is_ssl' => is_ssl(), 'fields' => array( 'banners' => true, 'reviews' => true, 'downloaded' => false, 'active_installs' => true ) ) ); if ( ! empty( $repo_data ) ) { $data = $repo_data; $data->wp_org_missing = false; } else { // Couldn't find plugin on .org. $selected_addon->is_wp_org_compliant = false; // Plugin is missing, not on Freemius nor WP.org. $data->wp_org_missing = true; } $data->fs_missing = ( ! $has_free_plan || $data->wp_org_missing ); } else { $data->has_purchased_license = false; $data->wp_org_missing = false; $fs_addon = null; $current_addon_version = false; if ( $this->_fs->is_addon_activated( $selected_addon->id ) ) { $fs_addon = $this->_fs->get_addon_instance( $selected_addon->id ); $current_addon_version = $fs_addon->get_plugin_version(); } else if ( $this->_fs->is_addon_installed( $selected_addon->id ) ) { $addon_plugin_data = get_plugin_data( ( WP_PLUGIN_DIR . '/' . $this->_fs->get_addon_basename( $selected_addon->id ) ), false, false ); if ( ! empty( $addon_plugin_data ) ) { $current_addon_version = $addon_plugin_data['Version']; } } // Fetch latest version from Freemius. $latest = $this->_fs->_fetch_latest_version( $selected_addon->id, true, FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION, $current_addon_version ); if ( $has_paid_plan ) { $blog_id = fs_request_get( 'fs_blog_id' ); $has_valid_blog_id = is_numeric( $blog_id ); if ( $has_valid_blog_id ) { switch_to_blog( $blog_id ); } $data->checkout_link = $this->_fs->checkout_url( WP_FS__PERIOD_ANNUALLY, false, array(), ( $has_valid_blog_id ? false : null ) ); if ( $has_valid_blog_id ) { restore_current_blog(); } } /** * Check if there's a purchased license in case the add-on can only be installed/downloaded as part of a purchased bundle. * * @author Leo Fajardo (@leorw) * @since 2.4.1 */ if ( is_object( $fs_addon ) ) { $data->has_purchased_license = $fs_addon->has_active_valid_license(); } else { $account_addons = $this->_fs->get_account_addons(); if ( ! empty( $account_addons ) && in_array( $selected_addon->id, $account_addons ) ) { $data->has_purchased_license = true; } } if ( $has_free_plan || $data->has_purchased_license ) { $data->download_link = $this->_fs->_get_latest_download_local_url( $selected_addon->id ); } $data->fs_missing = ( false === $latest && ( empty( $selected_addon->premium_releases_count ) || ! ( $selected_addon->premium_releases_count > 0 ) ) ); // Fetch as much as possible info from local files. $plugin_local_data = $this->_fs->get_plugin_data(); $data->author = $plugin_local_data['Author']; if ( ! empty( $selected_addon->info->banner_url ) ) { $data->banners = array( 'low' => $selected_addon->info->banner_url, ); } if ( ! empty( $selected_addon->info->screenshots ) ) { $view_vars = array( 'screenshots' => $selected_addon->info->screenshots, 'plugin' => $selected_addon, ); $data->sections['screenshots'] = fs_get_template( '/plugin-info/screenshots.php', $view_vars ); } if ( is_object( $latest ) ) { $data->version = $latest->version; $data->last_updated = $latest->created; $data->requires = $latest->requires_platform_version; $data->requires_php = $latest->requires_programming_language_version; $data->tested = $latest->tested_up_to_version; } else if ( ! empty( $current_addon_version ) ) { $data->version = $current_addon_version; } else { // Add dummy version. $data->version = '1.0.0'; // Add message to developer to deploy the plugin through Freemius. } } $data->name = $selected_addon->title; $view_vars = array( 'plugin' => $selected_addon ); if ( is_object( $latest ) && isset( $latest->readme ) && is_object( $latest->readme ) ) { $latest_version_readme_data = $latest->readme; if ( isset( $latest_version_readme_data->sections ) ) { $data->sections = (array) $latest_version_readme_data->sections; } else { $data->sections = array(); } } $data->sections['description'] = fs_get_template( '/plugin-info/description.php', $view_vars ); if ( $has_pricing ) { // Add plans to data. $data->plans = $plans; if ( $has_features ) { $view_vars = array( 'plans' => $plans, 'plugin' => $selected_addon, ); $data->sections['features'] = fs_get_template( '/plugin-info/features.php', $view_vars ); } } $data->has_free_plan = $has_free_plan; $data->has_paid_plan = $has_paid_plan; $data->is_paid = $has_paid_plan; $data->is_wp_org_compliant = $selected_addon->is_wp_org_compliant; $data->premium_slug = $selected_addon->premium_slug; $data->addon_id = $selected_addon->id; if ( ! isset( $data->has_purchased_license ) ) { $data->has_purchased_license = false; } return $data; } /** * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @param FS_Plugin_Plan $plan * * @return string */ private function get_billing_cycle( FS_Plugin_Plan $plan ) { $billing_cycle = null; if ( 1 === count( $plan->pricing ) && 1 == $plan->pricing[0]->licenses ) { $pricing = $plan->pricing[0]; if ( isset( $pricing->annual_price ) ) { $billing_cycle = 'annual'; } else if ( isset( $pricing->monthly_price ) ) { $billing_cycle = 'monthly'; } else if ( isset( $pricing->lifetime_price ) ) { $billing_cycle = 'lifetime'; } } else { foreach ( $plan->pricing as $pricing ) { if ( isset( $pricing->annual_price ) ) { $billing_cycle = 'annual'; } else if ( isset( $pricing->monthly_price ) ) { $billing_cycle = 'monthly'; } else if ( isset( $pricing->lifetime_price ) ) { $billing_cycle = 'lifetime'; } if ( ! is_null( $billing_cycle ) ) { break; } } } return $billing_cycle; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param FS_Plugin_Plan $plan * @param FS_Pricing $pricing * * @return float|null|string */ private function get_price_tag( FS_Plugin_Plan $plan, FS_Pricing $pricing ) { $price_tag = ''; if ( isset( $pricing->annual_price ) ) { $price_tag = $pricing->annual_price . ( $plan->is_block_features ? ' / year' : '' ); } else if ( isset( $pricing->monthly_price ) ) { $price_tag = $pricing->monthly_price . ' / mo'; } else if ( isset( $pricing->lifetime_price ) ) { $price_tag = $pricing->lifetime_price; } return '$' . $price_tag; } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param object $api * @param FS_Plugin_Plan $plan * * @return string */ private function get_actions_dropdown( $api, $plan = null ) { $this->actions = isset( $this->actions ) ? $this->actions : $this->get_plugin_actions( $api ); $actions = $this->actions; $checkout_cta = $this->get_checkout_cta( $api, $plan ); if ( ! empty( $checkout_cta ) ) { /** * If there's no license yet, make the checkout button the main CTA. Otherwise, make it the last item in * the actions dropdown. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ if ( ! $api->has_purchased_license ) { array_unshift( $actions, $checkout_cta ); } else { $actions[] = $checkout_cta; } } if ( empty( $actions ) ) { return ''; } $total_actions = count( $actions ); if ( 1 === $total_actions ) { return $actions[0]; } ob_start(); ?>
    checkout_link ) || ! isset( $api->plans ) || ! is_array( $api->plans ) || 0 == count( $api->plans ) ) { return ''; } if ( is_null( $plan ) ) { foreach ( $api->plans as $p ) { if ( ! empty( $p->pricing ) ) { $plan = $p; break; } } } $blog_id = fs_request_get( 'fs_blog_id' ); $has_valid_blog_id = is_numeric( $blog_id ); if ( $has_valid_blog_id ) { switch_to_blog( $blog_id ); } $addon_checkout_url = $this->_fs->addon_checkout_url( $plan->plugin_id, $plan->pricing[0]->id, $this->get_billing_cycle( $plan ), $plan->has_trial(), ( $has_valid_blog_id ? false : null ) ); if ( $has_valid_blog_id ) { restore_current_blog(); } return '' . esc_html( ! $plan->has_trial() ? ( $api->has_purchased_license ? fs_text_inline( 'Purchase More', 'purchase-more', $api->slug ) : fs_text_x_inline( 'Purchase', 'verb', 'purchase', $api->slug ) ) : sprintf( /* translators: %s: N-days trial */ fs_text_inline( 'Start my free %s', 'start-free-x', $api->slug ), $this->get_trial_period( $plan ) ) ) . ''; } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param object $api * * @return string[] */ private function get_plugin_actions( $api ) { $this->status = isset( $this->status ) ? $this->status : install_plugin_install_status( $api ); $is_update_available = ( 'update_available' === $this->status['status'] ); if ( $is_update_available && empty( $this->status['url'] ) ) { return array(); } $blog_id = fs_request_get( 'fs_blog_id' ); $active_plugins_directories_map = Freemius::get_active_plugins_directories_map( $blog_id ); $actions = array(); $is_addon_activated = $this->_fs->is_addon_activated( $api->slug ); $fs_addon = null; $is_free_installed = null; $is_premium_installed = null; $has_installed_version = ( 'install' !== $this->status['status'] ); if ( ! $api->has_paid_plan && ! $api->has_purchased_license ) { /** * Free-only add-on. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $is_free_installed = $has_installed_version; $is_premium_installed = false; } else if ( ! $api->has_free_plan ) { /** * Premium-only add-on. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $is_free_installed = false; $is_premium_installed = $has_installed_version; } else { /** * Freemium add-on. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ if ( ! $has_installed_version ) { $is_free_installed = false; $is_premium_installed = false; } else { $fs_addon = $is_addon_activated ? $this->_fs->get_addon_instance( $api->slug ) : null; if ( is_object( $fs_addon ) ) { if ( $fs_addon->is_premium() ) { $is_premium_installed = true; } else { $is_free_installed = true; } } if ( is_null( $is_free_installed ) ) { $is_free_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . "/{$api->slug}/{$api->slug}.php" ) ); if ( ! $is_free_installed ) { /** * Check if there's a plugin installed in a directory named `$api->slug`. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $installed_plugins = get_plugins( '/' . $api->slug ); $is_free_installed = ( ! empty( $installed_plugins ) ); } } if ( is_null( $is_premium_installed ) ) { $is_premium_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . "/{$api->premium_slug}/{$api->slug}.php" ) ); if ( ! $is_premium_installed ) { /** * Check if there's a plugin installed in a directory named `$api->premium_slug`. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $installed_plugins = get_plugins( '/' . $api->premium_slug ); $is_premium_installed = ( ! empty( $installed_plugins ) ); } } } $has_installed_version = ( $is_free_installed || $is_premium_installed ); } $this->status['is_free_installed'] = $is_free_installed; $this->status['is_premium_installed'] = $is_premium_installed; $can_install_free_version = false; $can_install_free_version_update = false; $can_download_free_version = false; $can_activate_free_version = false; $can_install_premium_version = false; $can_install_premium_version_update = false; $can_download_premium_version = false; $can_activate_premium_version = false; if ( ! $api->has_purchased_license ) { if ( $api->has_free_plan ) { if ( $has_installed_version ) { if ( $is_update_available ) { $can_install_free_version_update = true; } else if ( ! $is_premium_installed && ! isset( $active_plugins_directories_map[ dirname( $this->status['file'] ) ] ) ) { $can_activate_free_version = true; } } else { if ( $this->_fs->is_premium() || ! $this->_fs->is_org_repo_compliant() || $api->is_wp_org_compliant ) { $can_install_free_version = true; } else { $can_download_free_version = true; } } } } else { if ( ! is_object( $fs_addon ) && $is_addon_activated ) { $fs_addon = $this->_fs->get_addon_instance( $api->slug ); } $can_download_premium_version = true; if ( ! isset( $active_plugins_directories_map[ dirname( $this->status['file'] ) ] ) ) { if ( $is_premium_installed ) { $can_activate_premium_version = ( ! $is_addon_activated || ! $fs_addon->is_premium() ); } else if ( $is_free_installed ) { $can_activate_free_version = ( ! $is_addon_activated ); } } if ( $this->_fs->is_premium() || ! $this->_fs->is_org_repo_compliant() ) { if ( $is_update_available ) { $can_install_premium_version_update = true; } else if ( ! $is_premium_installed ) { $can_install_premium_version = true; } } } if ( $can_install_premium_version || $can_install_premium_version_update ) { if ( is_numeric( $blog_id ) ) { /** * Replace the network status URL with a blog admin–based status URL if the `Add-Ons` page is loaded * from a specific blog admin page (when `fs_blog_id` is valid) in order for plugin installation/update * to work. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $this->status['url'] = self::get_blog_status_url( $blog_id, $this->status['url'], $this->status['status'] ); } /** * Add the `fs_allow_updater_and_dialog` param to the install/update URL so that the add-on can be * installed/updated. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $this->status['url'] = str_replace( '?', '?fs_allow_updater_and_dialog=true&', $this->status['url'] ); } if ( $can_install_free_version_update || $can_install_premium_version_update ) { $actions[] = $this->get_cta( ( $can_install_free_version_update ? fs_esc_html_inline( 'Install Free Version Update Now', 'install-free-version-update-now', $api->slug ) : fs_esc_html_inline( 'Install Update Now', 'install-update-now', $api->slug ) ), true, false, $this->status['url'], '_parent' ); } else if ( $can_install_free_version || $can_install_premium_version ) { $actions[] = $this->get_cta( ( $can_install_free_version ? fs_esc_html_inline( 'Install Free Version Now', 'install-free-version-now', $api->slug ) : fs_esc_html_inline( 'Install Now', 'install-now', $api->slug ) ), true, false, $this->status['url'], '_parent' ); } $download_latest_action = ''; if ( ! empty( $api->download_link ) && ( $can_download_free_version || $can_download_premium_version ) ) { $download_latest_action = $this->get_cta( ( $can_download_free_version ? fs_esc_html_x_inline( 'Download Latest Free Version', 'as download latest version', 'download-latest-free-version', $api->slug ) : fs_esc_html_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $api->slug ) ), true, false, esc_url( $api->download_link ) ); } if ( ! $can_activate_free_version && ! $can_activate_premium_version ) { if ( ! empty( $download_latest_action ) ) { $actions[] = $download_latest_action; } } else { $activate_action = sprintf( '%s', wp_nonce_url( ( is_numeric( $blog_id ) ? trailingslashit( get_admin_url( $blog_id ) ) : '' ) . 'plugins.php?action=activate&plugin=' . $this->status['file'], 'activate-plugin_' . $this->status['file'] ), fs_esc_attr_inline( 'Activate this add-on', 'activate-this-addon', $api->slug ), $can_activate_free_version ? fs_text_inline( 'Activate Free Version', 'activate-free', $api->slug ) : fs_text_inline( 'Activate', 'activate', $api->slug ) ); if ( ! $can_download_premium_version && ! empty( $download_latest_action ) ) { $actions[] = $download_latest_action; $download_latest_action = ''; } if ( $can_install_premium_version || $can_install_premium_version_update ) { if ( $can_download_premium_version && ! empty( $download_latest_action ) ) { $actions[] = $download_latest_action; $download_latest_action = ''; } $actions[] = $activate_action; } else { array_unshift( $actions, $activate_action ); } if ( ! empty ($download_latest_action ) ) { $actions[] = $download_latest_action; } } return $actions; } /** * Rebuilds the status URL based on the admin URL. * * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param int $blog_id * @param string $network_status_url * @param string $status * * @return string */ private static function get_blog_status_url( $blog_id, $network_status_url, $status ) { if ( ! in_array( $status, array( 'install', 'update_available' ) ) ) { return $network_status_url; } $action = ( 'install' === $status ) ? 'install-plugin' : 'upgrade-plugin'; $url_params = fs_parse_url_params( $network_status_url, true ); if ( empty( $url_params ) || ! isset( $url_params['plugin'] ) ) { return $network_status_url; } $plugin = $url_params['plugin']; return wp_nonce_url( get_admin_url( $blog_id,"update.php?action={$action}&plugin={$plugin}"), "{$action}_{$plugin}"); } /** * Helper method to get a CTA button HTML. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $label * @param bool $is_primary * @param bool $is_disabled * @param string $href * @param string $target * * @return string */ private function get_cta( $label, $is_primary = true, $is_disabled = false, $href = '', $target = '_blank' ) { $classes = array(); if ( ! $is_primary ) { $classes[] = 'left'; } else { $classes[] = 'button-primary'; $classes[] = 'right'; } if ( $is_disabled ) { $classes[] = 'disabled'; } $rel = ( '_blank' === $target ) ? ' rel="noopener noreferrer"' : ''; return sprintf( '%s', empty( $href ) ? '' : 'href="' . $href . '" target="' . $target . '"' . $rel, implode( ' ', $classes ), $label ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @param FS_Plugin_Plan $plan * * @return string */ private function get_trial_period( $plan ) { $trial_period = (int) $plan->trial_period; switch ( $trial_period ) { case 30: return 'month'; case 60: return '2 months'; default: return "{$plan->trial_period} days"; } } /** * Display plugin information in dialog box form. * * Based on core install_plugin_information() function. * * @author Vova Feldman (@svovaf) * @since 1.0.6 */ function install_plugin_information() { global $tab; if ( empty( $_REQUEST['plugin'] ) ) { return; } $args = array( 'slug' => wp_unslash( $_REQUEST['plugin'] ), 'is_ssl' => is_ssl(), 'fields' => array( 'banners' => true, 'reviews' => true, 'downloaded' => false, 'active_installs' => true ) ); if ( is_array( $args ) ) { $args = (object) $args; } if ( ! isset( $args->per_page ) ) { $args->per_page = 24; } if ( ! isset( $args->locale ) ) { $args->locale = get_locale(); } $api = apply_filters( 'fs_plugins_api', false, 'plugin_information', $args ); if ( is_wp_error( $api ) ) { wp_die( $api ); } $plugins_allowedtags = array( 'a' => array( 'href' => array(), 'title' => array(), 'target' => array(), // Add image style for screenshots. 'class' => array() ), 'style' => array(), 'abbr' => array( 'title' => array() ), 'acronym' => array( 'title' => array() ), 'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(), 'div' => array( 'class' => array() ), 'span' => array( 'class' => array() ), 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array( 'class' => array() ), 'i' => array( 'class' => array() ), 'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(), 'img' => array( 'src' => array(), 'class' => array(), 'alt' => array() ), // 'table' => array(), // 'td' => array(), // 'tr' => array(), // 'th' => array(), // 'thead' => array(), // 'tbody' => array(), ); $plugins_section_titles = array( 'description' => fs_text_x_inline( 'Description', 'Plugin installer section title', 'description', $api->slug ), 'installation' => fs_text_x_inline( 'Installation', 'Plugin installer section title', 'installation', $api->slug ), 'faq' => fs_text_x_inline( 'FAQ', 'Plugin installer section title', 'faq', $api->slug ), 'screenshots' => fs_text_inline( 'Screenshots', 'screenshots', $api->slug ), 'changelog' => fs_text_x_inline( 'Changelog', 'Plugin installer section title', 'changelog', $api->slug ), 'reviews' => fs_text_x_inline( 'Reviews', 'Plugin installer section title', 'reviews', $api->slug ), 'other_notes' => fs_text_x_inline( 'Other Notes', 'Plugin installer section title', 'other-notes', $api->slug ), ); // Sanitize HTML // foreach ( (array) $api->sections as $section_name => $content ) { // $api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags ); // } foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) { if ( isset( $api->$key ) ) { $api->$key = wp_kses( $api->$key, $plugins_allowedtags ); } } // Add after $api->slug is ready. $plugins_section_titles['features'] = fs_text_x_inline( 'Features & Pricing', 'Plugin installer section title', 'features-and-pricing', $api->slug ); $_tab = esc_attr( $tab ); $section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English. if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) { $section_titles = array_keys( (array) $api->sections ); $section = array_shift( $section_titles ); } iframe_header( fs_text_inline( 'Plugin Install', 'plugin-install', $api->slug ) ); $_with_banner = ''; // var_dump($api->banners); if ( ! empty( $api->banners ) && ( ! empty( $api->banners['low'] ) || ! empty( $api->banners['high'] ) ) ) { $_with_banner = 'with-banner'; $low = empty( $api->banners['low'] ) ? $api->banners['high'] : $api->banners['low']; $high = empty( $api->banners['high'] ) ? $api->banners['low'] : $api->banners['high']; ?> '; echo "

    {$api->name}

    "; echo "
    \n"; foreach ( (array) $api->sections as $section_name => $content ) { if ( 'reviews' === $section_name && ( empty( $api->ratings ) || 0 === array_sum( (array) $api->ratings ) ) ) { continue; } if ( isset( $plugins_section_titles[ $section_name ] ) ) { $title = $plugins_section_titles[ $section_name ]; } else { $title = ucwords( str_replace( '_', ' ', $section_name ) ); } $class = ( $section_name === $section ) ? ' class="current"' : ''; $href = add_query_arg( array( 'tab' => $tab, 'section' => $section_name ) ); $href = esc_url( $href ); $san_section = esc_attr( $section_name ); echo "\t" . esc_html( $title ) . "\n"; } echo "
    \n"; ?>
    is_paid ) : ?> plans ) ) : ?>
    plans as $plan ) : ?> pricing ) ) { continue; } /** * @var FS_Plugin_Plan $plan */ ?> pricing[0] ?> is_multi_cycle() ?>

    slug ), $plan->title ) ) ?>

    has_annual() ?> has_monthly() ?>
    pricing[0]->annual_discount_percentage() : 0 ?> 0 ) : ?> slug ), $annual_discount . '%' ) ?>
    get_actions_dropdown( $api, $plan ) ?>
    has_trial() ) : ?> get_trial_period( $plan ) ?>
    • slug ), $trial_period ) ) ?>
    • slug ) ), $trial_period, '' . $this->get_price_tag( $plan, $plan->pricing[0] ) . '' ) ?>

    slug ) ?>

      version ) ) { ?>
    • slug ); ?> : version; ?>
    • author ) ) { ?>
    • slug ); ?> : author, '_blank' ); ?>
    • last_updated ) ) { ?>
    • slug ); ?> : slug ), human_time_diff( strtotime( $api->last_updated ) ) ) ) ?>
    • requires ) ) { ?>
    • slug ) ?> : slug ), $api->requires ) ) ?>
    • tested ) ) { ?>
    • slug ); ?> : tested; ?>
    • requires_php ) ) { ?>
    • slug ); ?>: slug ), $api->requires_php ) ); ?>
    • downloaded ) ) { ?>
    • slug ) ?> : downloaded ) ? /* translators: %s: 1 or One (Number of times downloaded) */ fs_text_inline( '%s time', 'x-time', $api->slug ) : /* translators: %s: Number of times downloaded */ fs_text_inline( '%s times', 'x-times', $api->slug ) ), number_format_i18n( $api->downloaded ) ) ); ?>
    • slug ) && true == $api->is_wp_org_compliant ) { ?>
    • slug ) ?> »
    • homepage ) ) { ?>
    • slug ) ?> »
    • donate_link ) && empty( $api->contributors ) ) { ?>
    • slug ) ?> »
    rating ) ) { ?>

    slug ); ?>

    $api->rating, 'type' => 'percent', 'number' => $api->num_ratings ) ); ?> (slug ), sprintf( ( ( 1 == $api->num_ratings ) ? /* translators: %s: 1 or One */ fs_text_inline( '%s rating', 'x-rating', $api->slug ) : /* translators: %s: Number larger than 1 */ fs_text_inline( '%s ratings', 'x-ratings', $api->slug ) ), number_format_i18n( $api->num_ratings ) ) ) ) ?>) ratings ) && array_sum( (array) $api->ratings ) > 0 ) { foreach ( $api->ratings as $key => $ratecount ) { // Avoid div-by-zero. $_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0; $stars_label = sprintf( ( ( 1 == $key ) ? /* translators: %s: 1 or One */ fs_text_inline( '%s star', 'x-star', $api->slug ) : /* translators: %s: Number larger than 1 */ fs_text_inline( '%s stars', 'x-stars', $api->slug ) ), number_format_i18n( $key ) ); ?>
    contributors ) ) { ?>

    slug ); ?>

      contributors as $contrib_username => $contrib_profile ) { if ( empty( $contrib_username ) && empty( $contrib_profile ) ) { continue; } if ( empty( $contrib_username ) ) { $contrib_username = preg_replace( '/^.+\/(.+)\/?$/', '\1', $contrib_profile ); } $contrib_username = sanitize_user( $contrib_username ); if ( empty( $contrib_profile ) ) { echo "
    • {$contrib_username}
    • "; } else { echo "
    • {$contrib_username}
    • "; } } ?>
    donate_link ) ) { ?> slug ) ?> »
    requires_php ) ? $api->requires_php : null; $requires_wp = isset( $api->requires ) ? $api->requires : null; $compatible_php = empty( $requires_php ) || version_compare( PHP_VERSION, $requires_php, '>=' ); // Strip off any -alpha, -RC, -beta, -src suffixes. list( $wp_version ) = explode( '-', $GLOBALS['wp_version'] ); $compatible_wp = empty( $requires_wp ) || version_compare( $wp_version, $requires_wp, '>=' ); $tested_wp = ( empty( $api->tested ) || version_compare( $wp_version, $api->tested, '<=' ) ); if ( ! $compatible_php ) { echo '

    ' . fs_text_inline( 'Error', 'error', $api->slug ) . ': ' . fs_text_inline( 'This plugin requires a newer version of PHP.', 'newer-php-required-error', $api->slug ); if ( current_user_can( 'update_php' ) ) { $wp_get_update_php_url = function_exists( 'wp_get_update_php_url' ) ? wp_get_update_php_url() : 'https://wordpress.org/support/update-php/'; printf( /* translators: %s: URL to Update PHP page. */ ' ' . fs_text_inline( 'Click here to learn more about updating PHP.', 'php-update-learn-more-link', $api->slug ), esc_url( $wp_get_update_php_url ) ); if ( function_exists( 'wp_update_php_annotation' ) ) { wp_update_php_annotation( '

    ', '' ); } } else { echo '

    '; } echo '
    '; } if ( ! $tested_wp ) { echo '

    ' . '' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ': ' . fs_text_inline( 'This plugin has not been tested with your current version of WordPress.', 'not-tested-warning', $api->slug ) . '

    '; } else if ( ! $compatible_wp ) { echo '

    ' . '' . fs_text_inline( 'Warning', 'warning', $api->slug ) . ': ' . fs_text_inline( 'This plugin has not been marked as compatible with your version of WordPress.', 'not-compatible-warning', $api->slug ) . '

    '; } foreach ( (array) $api->sections as $section_name => $content ) { $content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' ); $content = links_add_target( $content, '_blank' ); $san_section = esc_attr( $section_name ); $display = ( $section_name === $section ) ? 'block' : 'none'; if ( 'description' === $section_name && ( ( $api->is_wp_org_compliant && $api->wp_org_missing ) || ( ! $api->is_wp_org_compliant && $api->fs_missing ) ) ) { $missing_notice = array( 'type' => 'error', 'id' => md5( microtime() ), 'message' => $api->is_paid ? fs_text_inline( 'Paid add-on must be deployed to Freemius.', 'paid-addon-not-deployed', $api->slug ) : fs_text_inline( 'Add-on must be deployed to WordPress.org or Freemius.', 'free-addon-not-deployed', $api->slug ), ); fs_require_template( 'admin-notice.php', $missing_notice ); } echo "\t
    \n"; echo $content; echo "\t
    \n"; } echo "
    \n"; echo "
    \n"; echo "\n"; // #plugin-information-scrollable echo "\n"; ?> true, 'class' => true, 'style' => true, 'data-*' => true, ); return array( 'a' => array_merge( $common_attributes, array( 'href' => true, 'title' => true, 'target' => true, 'rel' => true, ) ), 'img' => array_merge( $common_attributes, array( 'src' => true, 'alt' => true, 'title' => true, 'width' => true, 'height' => true, ) ), 'br' => $common_attributes, 'em' => $common_attributes, 'small' => $common_attributes, 'strong' => $common_attributes, 'u' => $common_attributes, 'b' => $common_attributes, 'i' => $common_attributes, 'hr' => $common_attributes, 'span' => $common_attributes, 'p' => $common_attributes, 'div' => $common_attributes, 'ul' => $common_attributes, 'li' => $common_attributes, 'ol' => $common_attributes, 'h1' => $common_attributes, 'h2' => $common_attributes, 'h3' => $common_attributes, 'h4' => $common_attributes, 'h5' => $common_attributes, 'h6' => $common_attributes, 'button' => $common_attributes, 'sup' => $common_attributes, 'sub' => $common_attributes, 'nobr' => $common_attributes, ); } } if ( ! function_exists( 'fs_html_get_classname' ) ) { /** * Gets an HTML class attribute value. * * @param string|string[] $classes * * @return string */ function fs_html_get_classname( $classes ) { if ( is_array( $classes ) ) { $classes = implode( ' ', $classes ); } return esc_attr( $classes ); } } if ( ! function_exists( 'fs_html_get_attributes' ) ) { /** * Gets a properly escaped HTML attributes string from an associative array. * * @param array $attributes A key/value pair array of attributes. * * @return string */ function fs_html_get_attributes( $attributes ) { $attribute_string = ''; foreach ( $attributes as $key => $value ) { $attribute_string .= sprintf( ' %1$s="%2$s"', esc_attr( $key ), esc_attr( $value ) ); } return $attribute_string; } } if ( ! function_exists( 'fs_html_get_sanitized_html' ) ) { /** * Get sanitized HTML for template files. * * @param string $raw_html * * @return string * @since 2.5.10 */ function fs_html_get_sanitized_html( $raw_html ) { return wp_kses( $raw_html, fs_html_get_allowed_kses_list() ); } } freemius/includes/fs-essential-functions.php000064400000035054147600046700015320 0ustar00add( "Freemius failed to redirect the page because the headers have been already sent from line {$line} in file {$file}. If it's unexpected, it usually happens due to invalid space and/or EOL character(s).", 'Oops...', 'error' ); } return false; } if ( defined( 'DOING_AJAX' ) ) { // Don't redirect on AJAX calls. return false; } if ( ! $location ) // allows the wp_redirect filter to cancel a redirect { return false; } $location = fs_sanitize_redirect( $location ); if ( $is_IIS ) { header( "Refresh: 0;url=$location" ); } else { if ( php_sapi_name() != 'cgi-fcgi' ) { status_header( $status ); } // This causes problems on IIS and some FastCGI setups header( "Location: $location" ); } if ( $exit ) { exit(); } return true; } if ( ! function_exists( 'fs_sanitize_redirect' ) ) { /** * Sanitizes a URL for use in a redirect. * * @since 2.3 * * @param string $location * * @return string redirect-sanitized URL */ function fs_sanitize_redirect( $location ) { $location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!]|i', '', $location ); $location = fs_kses_no_null( $location ); // remove %0d and %0a from location $strip = array( '%0d', '%0a' ); $found = true; while ( $found ) { $found = false; foreach ( (array) $strip as $val ) { while ( strpos( $location, $val ) !== false ) { $found = true; $location = str_replace( $val, '', $location ); } } } return $location; } } if ( ! function_exists( 'fs_kses_no_null' ) ) { /** * Removes any NULL characters in $string. * * @since 1.0.0 * * @param string $string * * @return string */ function fs_kses_no_null( $string ) { $string = preg_replace( '/\0+/', '', $string ); $string = preg_replace( '/(\\\\0)+/', '', $string ); return $string; } } } #endregion Core Redirect (copied from BuddyPress) ----------------------------------------- if ( ! function_exists( 'fs_get_ip' ) ) { /** * Get server IP. * * @since 2.5.1 This method returns the server IP. * * @author Vova Feldman (@svovaf) * @since 1.1.2 * * @return string|null */ function fs_get_ip() { return empty( $_SERVER[ 'SERVER_ADDR' ] ) ? null : $_SERVER[ 'SERVER_ADDR' ]; } } if ( ! function_exists( 'fs_find_caller_plugin_file' ) ) { /** * Leverage backtrace to find caller plugin main file path. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return string */ function fs_find_caller_plugin_file() { /** * All the code below will be executed once on activation. * If the user changes the main plugin's file name, the file_exists() * will catch it. */ if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all_plugins = fs_get_plugins( true ); $all_plugins_paths = array(); // Get active plugin's main files real full names (might be symlinks). foreach ( $all_plugins as $relative_path => $data ) { $all_plugins_paths[] = fs_normalize_path( realpath( WP_PLUGIN_DIR . '/' . $relative_path ) ); } $plugin_file = null; for ( $i = 1, $bt = debug_backtrace(), $len = count( $bt ); $i < $len; $i ++ ) { if ( empty( $bt[ $i ]['file'] ) ) { continue; } if ( in_array( fs_normalize_path( $bt[ $i ]['file'] ), $all_plugins_paths ) ) { $plugin_file = $bt[ $i ]['file']; break; } } if ( is_null( $plugin_file ) ) { // Throw an error to the developer in case of some edge case dev environment. wp_die( 'Freemius SDK couldn\'t find the plugin\'s main file. Please contact sdk@freemius.com with the current error.', 'Error', array( 'back_link' => true ) ); } return $plugin_file; } } require_once dirname( __FILE__ ) . '/supplements/fs-essential-functions-1.1.7.1.php'; if ( ! function_exists( 'fs_update_sdk_newest_version' ) ) { /** * Update SDK newest version reference. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $sdk_relative_path * @param string|bool $plugin_file * * @global $fs_active_plugins */ function fs_update_sdk_newest_version( $sdk_relative_path, $plugin_file = false ) { /** * If there is a plugin running an older version of FS (1.2.1 or below), the `fs_update_sdk_newest_version()` * function in the older version will be used instead of this one. But since the older version is using * the `is_plugin_active` function to check if a plugin is active, passing the theme's `plugin_path` to the * `is_plugin_active` function will return false since the path is not a plugin path, so `in_activation` will be * `true` for theme modules and the upgrading of the SDK version to 1.2.2 or newer version will work fine. * * Future versions that will call this function will use the proper logic here instead of just relying on the * `is_plugin_active` function to fail for themes. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ global $fs_active_plugins; $newest_sdk = $fs_active_plugins->plugins[ $sdk_relative_path ]; if ( ! is_string( $plugin_file ) ) { $plugin_file = plugin_basename( fs_find_caller_plugin_file() ); } if ( ! isset( $newest_sdk->type ) || 'theme' !== $newest_sdk->type ) { if ( ! function_exists( 'is_plugin_active' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $in_activation = ( ! is_plugin_active( $plugin_file ) ); } else { $theme = wp_get_theme(); $in_activation = ( $newest_sdk->plugin_path == $theme->stylesheet ); } $fs_active_plugins->newest = (object) array( 'plugin_path' => $plugin_file, 'sdk_path' => $sdk_relative_path, 'version' => $newest_sdk->version, 'in_activation' => $in_activation, 'timestamp' => time(), ); // Update DB with latest SDK version and path. update_option( 'fs_active_plugins', $fs_active_plugins ); } } if ( ! function_exists( 'fs_newest_sdk_plugin_first' ) ) { /** * Reorder the plugins load order so the plugin with the newest Freemius SDK is loaded first. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @return bool Was plugin order changed. Return false if plugin was loaded first anyways. * * @global $fs_active_plugins */ function fs_newest_sdk_plugin_first() { global $fs_active_plugins; /** * @todo Multi-site network activated plugin are always loaded prior to site plugins so if there's a plugin activated in the network mode that has an older version of the SDK of another plugin which is site activated that has new SDK version, the fs-essential-functions.php will be loaded from the older SDK. Same thing about MU plugins (loaded even before network activated plugins). * * @link https://github.com/Freemius/wordpress-sdk/issues/26 */ $newest_sdk_plugin_path = $fs_active_plugins->newest->plugin_path; $active_plugins = get_option( 'active_plugins', array() ); $updated_active_plugins = array( $newest_sdk_plugin_path ); $plugin_found = false; $is_first_path = true; foreach ( $active_plugins as $key => $plugin_path ) { if ( $plugin_path === $newest_sdk_plugin_path ) { if ( $is_first_path ) { // if it's the first plugin already, no need to continue return false; } $plugin_found = true; // Skip the plugin (it is already added as the 1st item of $updated_active_plugins). continue; } $updated_active_plugins[] = $plugin_path; if ( $is_first_path ) { $is_first_path = false; } } if ( $plugin_found ) { update_option( 'active_plugins', $updated_active_plugins ); return true; } if ( is_multisite() ) { // Plugin is network active. $network_active_plugins = get_site_option( 'active_sitewide_plugins', array() ); if ( isset( $network_active_plugins[ $newest_sdk_plugin_path ] ) ) { reset( $network_active_plugins ); if ( $newest_sdk_plugin_path === key( $network_active_plugins ) ) { // Plugin is already activated first on the network level. return false; } else { $time = $network_active_plugins[ $newest_sdk_plugin_path ]; // Remove plugin from its current position. unset( $network_active_plugins[ $newest_sdk_plugin_path ] ); // Set it to be included first. $network_active_plugins = array( $newest_sdk_plugin_path => $time ) + $network_active_plugins; update_site_option( 'active_sitewide_plugins', $network_active_plugins ); return true; } } } return false; } } if ( ! function_exists( 'fs_fallback_to_newest_active_sdk' ) ) { /** * Go over all Freemius SDKs in the system and find and "remember" * the newest SDK which is associated with an active plugin. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @global $fs_active_plugins */ function fs_fallback_to_newest_active_sdk() { global $fs_active_plugins; /** * @var object $newest_sdk_data */ $newest_sdk_data = null; $newest_sdk_path = null; foreach ( $fs_active_plugins->plugins as $sdk_relative_path => $data ) { if ( is_null( $newest_sdk_data ) || version_compare( $data->version, $newest_sdk_data->version, '>' ) ) { // If plugin inactive or SDK starter file doesn't exist, remove SDK reference. if ( 'plugin' === $data->type ) { $is_module_active = is_plugin_active( $data->plugin_path ); } else { $active_theme = wp_get_theme(); $is_module_active = ( $data->plugin_path === $active_theme->get_template() ); } $is_sdk_exists = file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $sdk_relative_path . '/start.php' ) ); if ( ! $is_module_active || ! $is_sdk_exists ) { unset( $fs_active_plugins->plugins[ $sdk_relative_path ] ); // No need to store the data since it will be stored in fs_update_sdk_newest_version() // or explicitly with update_option(). } else { $newest_sdk_data = $data; $newest_sdk_path = $sdk_relative_path; } } } if ( is_null( $newest_sdk_data ) ) { // Couldn't find any SDK reference. $fs_active_plugins = new stdClass(); update_option( 'fs_active_plugins', $fs_active_plugins ); } else { fs_update_sdk_newest_version( $newest_sdk_path, $newest_sdk_data->plugin_path ); } } }freemius/includes/fs-core-functions.php000064400000146124147600046700014262 0ustar00 $value ) { $input[ $key ] = fs_sanitize_input( $value ); } } else { // Allow empty values to pass through as-is, like `null`, `''`, `0`, `'0'` etc. $input = empty( $input ) ? $input : sanitize_text_field( $input ); } return $input; } } if ( ! function_exists( 'fs_request_get' ) ) { /** * A helper method to fetch GET/POST user input with an optional default value when the input is not set. * * @author Vova Feldman (@svovaf) * * @note The return value is always sanitized with sanitize_text_field(). * * @param string $key * @param mixed $def * @param string|bool $type Since 1.2.1.7 - when set to 'get' will look for the value passed via querystring, when * set to 'post' will look for the value passed via the POST request's body, otherwise, * will check if the parameter was passed in any of the two. * * * @return mixed */ function fs_request_get( $key, $def = false, $type = false ) { return fs_sanitize_input( fs_request_get_raw( $key, $def, $type ) ); } } if ( ! function_exists( 'fs_request_has' ) ) { function fs_request_has( $key ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return isset( $_REQUEST[ $key ] ); } } if ( ! function_exists( 'fs_request_get_bool' ) ) { /** * A helper method to fetch GET/POST user boolean input with an optional default value when the input is not set. * * @author Vova Feldman (@svovaf) * * @param string $key * @param bool $def * * @return bool|mixed */ function fs_request_get_bool( $key, $def = false ) { $val = fs_request_get( $key, null ); if ( is_null( $val ) ) { return $def; } if ( is_bool( $val ) ) { return $val; } else if ( is_numeric( $val ) ) { if ( 1 == $val ) { return true; } else if ( 0 == $val ) { return false; } } else if ( is_string( $val ) ) { $val = strtolower( $val ); if ( 'true' === $val ) { return true; } else if ( 'false' === $val ) { return false; } } return $def; } } if ( ! function_exists( 'fs_request_is_post' ) ) { function fs_request_is_post() { return ( 'post' === strtolower( $_SERVER['REQUEST_METHOD'] ) ); } } if ( ! function_exists( 'fs_request_is_get' ) ) { function fs_request_is_get() { return ( 'get' === strtolower( $_SERVER['REQUEST_METHOD'] ) ); } } if ( ! function_exists( 'fs_get_action' ) ) { function fs_get_action( $action_key = 'action' ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended if ( ! empty( $_REQUEST[ $action_key ] ) && is_string( $_REQUEST[ $action_key ] ) ) { return strtolower( $_REQUEST[ $action_key ] ); } if ( 'action' == $action_key ) { $action_key = 'fs_action'; if ( ! empty( $_REQUEST[ $action_key ] ) && is_string( $_REQUEST[ $action_key ] ) ) { return strtolower( $_REQUEST[ $action_key ] ); } } return false; // phpcs:enable WordPress.Security.NonceVerification.Recommended } } if ( ! function_exists( 'fs_request_is_action' ) ) { function fs_request_is_action( $action, $action_key = 'action' ) { return ( strtolower( $action ) === fs_get_action( $action_key ) ); } } if ( ! function_exists( 'fs_request_is_action_secure' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.0.0 * * @since 1.2.1.5 Allow nonce verification. * * @param string $action * @param string $action_key * @param string $nonce_key * * @return bool */ function fs_request_is_action_secure( $action, $action_key = 'action', $nonce_key = 'nonce' ) { if ( strtolower( $action ) !== fs_get_action( $action_key ) ) { return false; } $nonce = ! empty( $_REQUEST[ $nonce_key ] ) ? $_REQUEST[ $nonce_key ] : ''; if ( empty( $nonce ) || ( false === wp_verify_nonce( $nonce, $action ) ) ) { return false; } return true; } } #endregion if ( ! function_exists( 'fs_is_plugin_page' ) ) { function fs_is_plugin_page( $page_slug ) { return ( is_admin() && $page_slug === fs_request_get( 'page' ) ); } } if ( ! function_exists( 'fs_get_raw_referer' ) ) { /** * Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer. * * Do not use for redirects, use {@see wp_get_referer()} instead. * * @since 1.2.3 * * @return string|false Referer URL on success, false on failure. */ function fs_get_raw_referer() { if ( function_exists( 'wp_get_raw_referer' ) ) { return wp_get_raw_referer(); } if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) { return wp_unslash( $_REQUEST['_wp_http_referer'] ); } else if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) { return wp_unslash( $_SERVER['HTTP_REFERER'] ); } return false; } } /* Core UI. --------------------------------------------------------------------------------------------*/ if ( ! function_exists( 'fs_ui_action_button' ) ) { /** * @param number $module_id * @param string $page * @param string $action * @param string $title * @param string $button_class * @param array $params * @param bool $is_primary * @param bool $is_small * @param string|bool $icon_class Optional class for an icon (since 1.1.7). * @param string|bool $confirmation Optional confirmation message before submit (since 1.1.7). * @param string $method Since 1.1.7 * * @uses fs_ui_get_action_button() */ function fs_ui_action_button( $module_id, $page, $action, $title, $button_class = '', $params = array(), $is_primary = true, $is_small = false, $icon_class = false, $confirmation = false, $method = 'GET' ) { echo fs_ui_get_action_button( $module_id, $page, $action, $title, $button_class, $params, $is_primary, $is_small, $icon_class, $confirmation, $method ); } } if ( ! function_exists( 'fs_ui_get_action_button' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @param number $module_id * @param string $page * @param string $action * @param string $title * @param string $button_class * @param array $params * @param bool $is_primary * @param bool $is_small * @param string|bool $icon_class Optional class for an icon. * @param string|bool $confirmation Optional confirmation message before submit. * @param string $method * * @return string */ function fs_ui_get_action_button( $module_id, $page, $action, $title, $button_class = '', $params = array(), $is_primary = true, $is_small = false, $icon_class = false, $confirmation = false, $method = 'GET' ) { // Prepend icon (if set). $title = ( is_string( $icon_class ) ? ' ' : '' ) . $title; if ( is_string( $confirmation ) ) { return sprintf( '
    %s%s
    ', freemius( $module_id )->_get_admin_page_url( $page, $params ), $method, $action, wp_nonce_field( $action, '_wpnonce', true, false ), 'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ), $confirmation, $title ); } else if ( 'GET' !== strtoupper( $method ) ) { return sprintf( '
    %s%s
    ', freemius( $module_id )->_get_admin_page_url( $page, $params ), $method, $action, wp_nonce_field( $action, '_wpnonce', true, false ), 'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ), $title ); } else { return sprintf( '%s', wp_nonce_url( freemius( $module_id )->_get_admin_page_url( $page, array_merge( $params, array( 'fs_action' => $action ) ) ), $action ), 'button' . ( ! empty( $button_class ) ? ' ' . $button_class : '' ) . ( $is_primary ? ' button-primary' : '' ) . ( $is_small ? ' button-small' : '' ), $title ); } } function fs_ui_action_link( $module_id, $page, $action, $title, $params = array() ) { ?> $entities_or_entity ) { if ( is_array( $entities_or_entity ) ) { $entities[ $key ] = fs_get_entities( $entities_or_entity, $class_name ); } else { $entities[ $key ] = fs_get_entity( $entities_or_entity, $class_name ); } } return $entities; } } if ( ! function_exists( 'fs_nonce_url' ) ) { /** * Retrieve URL with nonce added to URL query. * * Originally was using `wp_nonce_url()` but the new version * changed the return value to escaped URL, that's not the expected * behaviour. * * @author Vova Feldman (@svovaf) * @since ~1.1.3 * * @param string $actionurl URL to add nonce action. * @param int|string $action Optional. Nonce action name. Default -1. * @param string $name Optional. Nonce name. Default '_wpnonce'. * * @return string Escaped URL with nonce action added. */ function fs_nonce_url( $actionurl, $action = - 1, $name = '_wpnonce' ) { return add_query_arg( $name, wp_create_nonce( $action ), $actionurl ); } } if ( ! function_exists( 'fs_parse_url_params' ) ) { /** * Returns the query parameters of the given URL if there are any. * * @param string $url * @param bool $html_entity_decode * * @return array Key value pair where key represents the parameter name and value represents the parameter value. */ function fs_parse_url_params( $url, $html_entity_decode = false ) { $query_str = parse_url( $url, PHP_URL_QUERY ); $url_params = array(); if ( empty( $query_str ) ) { return $url_params; } if ( $html_entity_decode ) { $query_str = html_entity_decode( $query_str ); } parse_str( $query_str, $url_params ); return $url_params; } } if ( ! function_exists( 'fs_starts_with' ) ) { /** * Check if string starts with. * * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param string $haystack * @param string $needle * * @return bool */ function fs_starts_with( $haystack, $needle ) { $length = strlen( $needle ); return ( substr( $haystack, 0, $length ) === $needle ); } } if ( ! function_exists( 'fs_ends_with' ) ) { /** * Check if string ends with. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $haystack * @param string $needle * * @return bool */ function fs_ends_with( $haystack, $needle ) { $length = strlen( $needle ); $start = $length * - 1; // negative return ( substr( $haystack, $start ) === $needle ); } } if ( ! function_exists( 'fs_strip_url_protocol' ) ) { function fs_strip_url_protocol( $url ) { if ( ! fs_starts_with( $url, 'http' ) ) { return $url; } $protocol_pos = strpos( $url, '://' ); if ( $protocol_pos > 5 ) { return $url; } return substr( $url, $protocol_pos + 3 ); } } #region Url Canonization ------------------------------------------------------------------ if ( ! function_exists( 'fs_canonize_url' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param string $url * @param bool $omit_host * @param array $ignore_params * * @return string */ function fs_canonize_url( $url, $omit_host = false, $ignore_params = array() ) { $parsed_url = parse_url( strtolower( $url ) ); // if ( ! isset( $parsed_url['host'] ) ) { // return $url; // } $canonical = ( ( $omit_host || ! isset( $parsed_url['host'] ) ) ? '' : $parsed_url['host'] ) . $parsed_url['path']; if ( isset( $parsed_url['query'] ) ) { parse_str( $parsed_url['query'], $queryString ); $canonical .= '?' . fs_canonize_query_string( $queryString, $ignore_params ); } return $canonical; } } if ( ! function_exists( 'fs_canonize_query_string' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param array $params * @param array $ignore_params * @param bool $params_prefix * * @return string */ function fs_canonize_query_string( array $params, array &$ignore_params, $params_prefix = false ) { if ( ! is_array( $params ) || 0 === count( $params ) ) { return ''; } // Url encode both keys and values $keys = fs_urlencode_rfc3986( array_keys( $params ) ); $values = fs_urlencode_rfc3986( array_values( $params ) ); $params = array_combine( $keys, $values ); // Parameters are sorted by name, using lexicographical byte value ordering. // Ref: Spec: 9.1.1 (1) uksort( $params, 'strcmp' ); $pairs = array(); foreach ( $params as $parameter => $value ) { $lower_param = strtolower( $parameter ); // Skip ignore params. if ( in_array( $lower_param, $ignore_params ) || ( false !== $params_prefix && fs_starts_with( $lower_param, $params_prefix ) ) ) { continue; } if ( is_array( $value ) ) { // If two or more parameters share the same name, they are sorted by their value // Ref: Spec: 9.1.1 (1) natsort( $value ); foreach ( $value as $duplicate_value ) { $pairs[] = $lower_param . '=' . $duplicate_value; } } else { $pairs[] = $lower_param . '=' . $value; } } if ( 0 === count( $pairs ) ) { return ''; } return implode( "&", $pairs ); } } if ( ! function_exists( 'fs_urlencode_rfc3986' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param string|string[] $input * * @return array|mixed|string */ function fs_urlencode_rfc3986( $input ) { if ( is_array( $input ) ) { return array_map( 'fs_urlencode_rfc3986', $input ); } else if ( is_scalar( $input ) ) { return str_replace( '+', ' ', str_replace( '%7E', '~', rawurlencode( $input ) ) ); } return ''; } } #endregion Url Canonization ------------------------------------------------------------------ if ( ! function_exists( 'fs_download_image' ) ) { /** * @author Vova Feldman (@svovaf) * * @since 1.2.2 Changed to usage of WP_Filesystem_Direct. * * @param string $from URL * @param string $to File path. * * @return bool Is successfully downloaded. */ function fs_download_image( $from, $to ) { $dir = dirname( $to ); if ( 'direct' !== get_filesystem_method( array(), $dir ) ) { return false; } if ( ! class_exists( 'WP_Filesystem_Direct' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; } $fs = new WP_Filesystem_Direct( '' ); $tmpfile = download_url( $from ); if ( $tmpfile instanceof WP_Error ) { // Issue downloading the file. return false; } $fs->copy( $tmpfile, $to ); $fs->delete( $tmpfile ); return true; } } /* General Utilities --------------------------------------------------------------------------------------------*/ if ( ! function_exists( 'fs_sort_by_priority' ) ) { /** * Sorts an array by the value of the priority key. * * @author Daniel Iser (@danieliser) * @since 1.1.7 * * @param $a * @param $b * * @return int */ function fs_sort_by_priority( $a, $b ) { // If b has a priority and a does not, b wins. if ( ! isset( $a['priority'] ) && isset( $b['priority'] ) ) { return 1; } // If b has a priority and a does not, b wins. elseif ( isset( $a['priority'] ) && ! isset( $b['priority'] ) ) { return - 1; } // If neither has a priority or both priorities are equal it's a tie. elseif ( ( ! isset( $a['priority'] ) && ! isset( $b['priority'] ) ) || $a['priority'] === $b['priority'] ) { return 0; } // If both have priority return the winner. return ( $a['priority'] < $b['priority'] ) ? - 1 : 1; } } #-------------------------------------------------------------------------------- #region Localization #-------------------------------------------------------------------------------- global $fs_text_overrides; if ( ! isset( $fs_text_overrides ) ) { $fs_text_overrides = array(); } if ( ! function_exists( 'fs_text' ) ) { /** * Retrieve a translated text by key. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $key * @param string $slug * * @return string * * @global $fs_text_overrides */ function fs_text( $key, $slug = 'freemius' ) { global $fs_text_overrides; if ( isset( $fs_text_overrides[ $slug ] ) ) { if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) { return $fs_text_overrides[ $slug ][ $key ]; } $lower_key = strtolower( $key ); if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) { return $fs_text_overrides[ $slug ][ $lower_key ]; } } return $key; } #region Private /** * Retrieve an inline translated text by key with a context. * * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string * * @global $fs_text_overrides */ function _fs_text_x_inline( $text, $context, $key = '', $slug = 'freemius' ) { list( $text, $text_domain ) = fs_text_and_domain( $text, $key, $slug ); // Avoid misleading Theme Check warning. $fn = 'translate_with_gettext_context'; return $fn( $text, $context, $text_domain ); } #endregion /** * Retrieve an inline translated text by key with a context. * * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string * * @global $fs_text_overrides */ function fs_text_x_inline( $text, $context, $key = '', $slug = 'freemius' ) { return _fs_text_x_inline( $text, $context, $key, $slug ); } /** * Output a translated text by key. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $key * @param string $slug */ function fs_echo( $key, $slug = 'freemius' ) { echo fs_text( $key, $slug ); } /** * Output an inline translated text. * * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. */ function fs_echo_inline( $text, $key = '', $slug = 'freemius' ) { echo _fs_text_inline( $text, $key, $slug ); } /** * Output an inline translated text with a context. * * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. */ function fs_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) { echo _fs_text_x_inline( $text, $context, $key, $slug ); } } if ( ! function_exists( 'fs_text_override' ) ) { /** * Get a translatable text override if exists, or `false`. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string|false */ function fs_text_override( $text, $key, $slug ) { global $fs_text_overrides; /** * Check if string is overridden. */ if ( ! isset( $fs_text_overrides[ $slug ] ) ) { return false; } if ( empty( $key ) ) { $key = strtolower( str_replace( ' ', '-', $text ) ); } if ( isset( $fs_text_overrides[ $slug ][ $key ] ) ) { return $fs_text_overrides[ $slug ][ $key ]; } $lower_key = strtolower( $key ); if ( isset( $fs_text_overrides[ $slug ][ $lower_key ] ) ) { return $fs_text_overrides[ $slug ][ $lower_key ]; } return false; } } if ( ! function_exists( 'fs_text_and_domain' ) ) { /** * Get a translatable text and its text domain. * * When the text is overridden by the module, returns the overridden text and the text domain of the module. Otherwise, returns the original text and 'freemius' as the text domain. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string[] */ function fs_text_and_domain( $text, $key, $slug ) { $override = fs_text_override( $text, $key, $slug ); if ( false === $override ) { // No override, use FS text domain. $text_domain = 'freemius'; } else { // Found an override. $text = $override; // Use the module's text domain. $text_domain = $slug; } return array( $text, $text_domain ); } } if ( ! function_exists( '_fs_text_inline' ) ) { /** * Retrieve an inline translated text by key. * * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string * * @global $fs_text_overrides */ function _fs_text_inline( $text, $key = '', $slug = 'freemius' ) { list( $text, $text_domain ) = fs_text_and_domain( $text, $key, $slug ); // Avoid misleading Theme Check warning. $fn = 'translate'; return $fn( $text, $text_domain ); } } if ( ! function_exists( 'fs_text_inline' ) ) { /** * Retrieve an inline translated text by key. * * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string * * @global $fs_text_overrides */ function fs_text_inline( $text, $key = '', $slug = 'freemius' ) { return _fs_text_inline( $text, $key, $slug ); } } if ( ! function_exists( 'fs_esc_attr' ) ) { /** * @author Vova Feldman * @since 1.2.1.6 * * @param string $key * @param string $slug * * @return string */ function fs_esc_attr( $key, $slug ) { return esc_attr( fs_text( $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_attr_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string */ function fs_esc_attr_inline( $text, $key = '', $slug = 'freemius' ) { return esc_attr( _fs_text_inline( $text, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_attr_x_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string */ function fs_esc_attr_x_inline( $text, $context, $key = '', $slug = 'freemius' ) { return esc_attr( _fs_text_x_inline( $text, $context, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_attr_echo' ) ) { /** * @author Vova Feldman * @since 1.2.1.6 * * @param string $key * @param string $slug */ function fs_esc_attr_echo( $key, $slug ) { echo esc_attr( fs_text( $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_attr_echo_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. */ function fs_esc_attr_echo_inline( $text, $key = '', $slug = 'freemius' ) { echo esc_attr( _fs_text_inline( $text, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_js' ) ) { /** * @author Vova Feldman * @since 1.2.1.6 * * @param string $key * @param string $slug * * @return string */ function fs_esc_js( $key, $slug ) { return esc_js( fs_text( $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_js_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string */ function fs_esc_js_inline( $text, $key = '', $slug = 'freemius' ) { return esc_js( _fs_text_inline( $text, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_js_x_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string */ function fs_esc_js_x_inline( $text, $context, $key = '', $slug = 'freemius' ) { return esc_js( _fs_text_x_inline( $text, $context, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_js_echo_x_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return void */ function fs_esc_js_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) { echo esc_js( _fs_text_x_inline( $text, $context, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_js_echo' ) ) { /** * @author Vova Feldman * @since 1.2.1.6 * * @param string $key * @param string $slug */ function fs_esc_js_echo( $key, $slug ) { echo esc_js( fs_text( $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_js_echo_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. */ function fs_esc_js_echo_inline( $text, $key = '', $slug = 'freemius' ) { echo esc_js( _fs_text_inline( $text, $key, $slug ) ); } } if ( ! function_exists( 'fs_json_encode_echo' ) ) { /** * @author Vova Feldman * @since 1.2.1.6 * * @param string $key * @param string $slug */ function fs_json_encode_echo( $key, $slug ) { echo json_encode( fs_text( $key, $slug ) ); } } if ( ! function_exists( 'fs_json_encode_echo_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. */ function fs_json_encode_echo_inline( $text, $key = '', $slug = 'freemius' ) { echo json_encode( _fs_text_inline( $text, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_html' ) ) { /** * @author Vova Feldman * @since 1.2.1.6 * * @param string $key * @param string $slug * * @return string */ function fs_esc_html( $key, $slug ) { return esc_html( fs_text( $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_html_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string */ function fs_esc_html_inline( $text, $key = '', $slug = 'freemius' ) { return esc_html( _fs_text_inline( $text, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_html_x_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. * * @return string */ function fs_esc_html_x_inline( $text, $context, $key = '', $slug = 'freemius' ) { return esc_html( _fs_text_x_inline( $text, $context, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_html_echo_x_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. */ function fs_esc_html_echo_x_inline( $text, $context, $key = '', $slug = 'freemius' ) { echo esc_html( _fs_text_x_inline( $text, $context, $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_html_echo' ) ) { /** * @author Vova Feldman * @since 1.2.1.6 * * @param string $key * @param string $slug */ function fs_esc_html_echo( $key, $slug ) { echo esc_html( fs_text( $key, $slug ) ); } } if ( ! function_exists( 'fs_esc_html_echo_inline' ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * @param string $slug Module slug for overrides. */ function fs_esc_html_echo_inline( $text, $key = '', $slug = 'freemius' ) { echo esc_html( _fs_text_inline( $text, $key, $slug ) ); } } if ( ! function_exists( 'fs_override_i18n' ) ) { /** * Override default i18n text phrases. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param array[string]string $key_value * @param string $slug * * @global $fs_text_overrides */ function fs_override_i18n( array $key_value, $slug = 'freemius' ) { global $fs_text_overrides; if ( ! isset( $fs_text_overrides[ $slug ] ) ) { $fs_text_overrides[ $slug ] = array(); } foreach ( $key_value as $key => $value ) { $fs_text_overrides[ $slug ][ $key ] = $value; } } } #endregion #-------------------------------------------------------------------------------- #region Multisite Network #-------------------------------------------------------------------------------- if ( ! function_exists( 'fs_is_plugin_uninstall' ) ) { /** * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function fs_is_plugin_uninstall() { return ( defined( 'WP_UNINSTALL_PLUGIN' ) || ( 0 < did_action( 'pre_uninstall_plugin' ) ) ); } } if ( ! function_exists( 'fs_is_network_admin' ) ) { /** * Unlike is_network_admin(), this one will also work properly when * the context execution is WP AJAX handler, and during plugin * uninstall. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function fs_is_network_admin() { return ( WP_FS__IS_NETWORK_ADMIN || ( is_multisite() && fs_is_plugin_uninstall() ) ); } } if ( ! function_exists( 'fs_is_blog_admin' ) ) { /** * Unlike is_blog_admin(), this one will also work properly when * the context execution is WP AJAX handler, and during plugin * uninstall. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function fs_is_blog_admin() { return ( WP_FS__IS_BLOG_ADMIN || ( ! is_multisite() && fs_is_plugin_uninstall() ) ); } } #endregion if ( ! function_exists( 'fs_apply_filter' ) ) { /** * Apply filter for specific plugin. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string $module_unique_affix Module's unique affix. * @param string $tag The name of the filter hook. * @param mixed $value The value on which the filters hooked to `$tag` are applied on. * * @return mixed The filtered value after all hooked functions are applied to it. * * @uses apply_filters() */ function fs_apply_filter( $module_unique_affix, $tag, $value ) { $args = func_get_args(); return call_user_func_array( 'apply_filters', array_merge( array( "fs_{$tag}_{$module_unique_affix}" ), array_slice( $args, 2 ) ) ); } } if ( ! function_exists( 'fs_get_optional_constant' ) ) { /** * Gets the value of an optional constant. If the constant is not defined, the default value will be returned. * * @author Swashata Ghosh (@swashata) * @since 2.5.12.5 * * @param string $constant_name * @param mixed $default_value * * @return mixed */ function fs_get_optional_constant( $constant_name, $default_value = null ) { return defined( $constant_name ) ? constant( $constant_name ) : $default_value; } } freemius/includes/class-fs-user-lock.php000064400000004423147600046700014326 0ustar00_lock = new FS_Lock( "locked_{$current_user_id}" ); } /** * Try to acquire lock. If the lock is already set or is being acquired by another locker, don't do anything. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @param int $expiration * * @return bool TRUE if successfully acquired lock. */ function try_lock( $expiration = 0 ) { return $this->_lock->try_lock( $expiration ); } /** * Acquire lock regardless if it's already acquired by another locker or not. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @param int $expiration */ function lock( $expiration = 0 ) { $this->_lock->lock( $expiration ); } /** * Unlock the lock. * * @author Vova Feldman (@svovaf) * @since 2.1.0 */ function unlock() { $this->_lock->unlock(); } }freemius/includes/class-fs-storage.php000064400000054360147600046700014073 0ustar00_module_type = $module_type; $this->_module_slug = $slug; $this->_is_multisite = is_multisite(); if ( $this->_is_multisite ) { $this->_blog_id = get_current_blog_id(); $this->_network_storage = FS_Key_Value_Storage::instance( $module_type . '_data', $slug, true ); } $this->_storage = FS_Key_Value_Storage::instance( $module_type . '_data', $slug, $this->_blog_id ); } /** * Tells this storage wrapper class that the context plugin is network active. This flag will affect how values * are retrieved/stored from/into the storage. * * @author Leo Fajardo (@leorw) * * @param bool $is_network_active * @param bool $is_delegated_connection */ function set_network_active( $is_network_active = true, $is_delegated_connection = false ) { $this->_is_network_active = $is_network_active; $this->_is_delegated_connection = $is_delegated_connection; } /** * Switch the context of the site level storage manager. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id */ function set_site_blog_context( $blog_id ) { $this->_storage = $this->get_site_storage( $blog_id ); $this->_blog_id = $blog_id; } /** * @author Leo Fajardo (@leorw) * * @param string $key * @param mixed $value * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP). * @param int $option_level Since 2.5.1 * @param bool $flush */ function store( $key, $value, $network_level_or_blog_id = null, $option_level = self::OPTION_LEVEL_UNDEFINED, $flush = true ) { if ( $this->should_use_network_storage( $key, $network_level_or_blog_id, $option_level ) ) { $this->_network_storage->store( $key, $value, $flush ); } else { $storage = $this->get_site_storage( $network_level_or_blog_id ); $storage->store( $key, $value, $flush ); } } /** * @author Leo Fajardo (@leorw) * * @param bool $store * @param string[] $exceptions Set of keys to keep and not clear. * @param int|null|bool $network_level_or_blog_id */ function clear_all( $store = true, $exceptions = array(), $network_level_or_blog_id = null ) { if ( ! $this->_is_multisite || false === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) || is_numeric( $network_level_or_blog_id ) ) { $storage = $this->get_site_storage( $network_level_or_blog_id ); $storage->clear_all( $store, $exceptions ); } if ( $this->_is_multisite && ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) ) ) { $this->_network_storage->clear_all( $store, $exceptions ); } } /** * @author Leo Fajardo (@leorw) * * @param string $key * @param bool $store * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP). */ function remove( $key, $store = true, $network_level_or_blog_id = null ) { if ( $this->should_use_network_storage( $key, $network_level_or_blog_id ) ) { $this->_network_storage->remove( $key, $store ); } else { $storage = $this->get_site_storage( $network_level_or_blog_id ); $storage->remove( $key, $store ); } } /** * @author Leo Fajardo (@leorw) * * @param string $key * @param mixed $default * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP). * @param int $option_level Since 2.5.1 * * @return mixed */ function get( $key, $default = false, $network_level_or_blog_id = null, $option_level = self::OPTION_LEVEL_UNDEFINED ) { if ( $this->should_use_network_storage( $key, $network_level_or_blog_id, $option_level ) ) { return $this->_network_storage->get( $key, $default ); } else { $storage = $this->get_site_storage( $network_level_or_blog_id ); return $storage->get( $key, $default ); } } /** * Multisite activated: * true: Save network storage. * int: Save site specific storage. * false|0: Save current site storage. * null: Save network and current site storage. * Site level activated: * Save site storage. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param bool|int|null $network_level_or_blog_id */ function save( $network_level_or_blog_id = null ) { if ( $this->_is_network_active && ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) ) ) { $this->_network_storage->save(); } if ( ! $this->_is_network_active || true !== $network_level_or_blog_id ) { $storage = $this->get_site_storage( $network_level_or_blog_id ); $storage->save(); } } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return string */ function get_module_slug() { return $this->_module_slug; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return string */ function get_module_type() { return $this->_module_type; } /** * Migration script to the new storage data structure that is network compatible. * * IMPORTANT: * This method should be executed only after it is determined if this is a network * level compatible product activation. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function migrate_to_network() { if ( ! $this->_is_multisite ) { return; } $updated = false; if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) { self::load_network_options_map(); } foreach ( self::$_NETWORK_OPTIONS_MAP as $option => $storage_level ) { if ( ! $this->is_multisite_option( $option ) ) { continue; } if ( isset( $this->_storage->{$option} ) && ! isset( $this->_network_storage->{$option} ) ) { // Migrate option to the network storage. $this->_network_storage->store( $option, $this->_storage->{$option}, false ); $updated = true; } } if ( ! $updated ) { return; } // Update network level storage. $this->_network_storage->save(); // $this->_storage->save(); } #-------------------------------------------------------------------------------- #region Helper Methods #-------------------------------------------------------------------------------- /** * We don't want to load the map right away since it's not even needed in a non-MS environment. * * Example: * array( * 'option1' => 0, // Means that the option should always be stored on the network level. * 'option2' => 1, // Means that the option should be stored on the network level only when the module was network level activated. * 'option2' => 2, // Means that the option should be stored on the network level only when the module was network level activated AND the connection was NOT delegated. * 'option3' => 3, // Means that the option should always be stored on the site level. * ) * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ private static function load_network_options_map() { self::$_NETWORK_OPTIONS_MAP = array( // Network level options. 'affiliate_application_data' => self::OPTION_LEVEL_NETWORK, 'beta_data' => self::OPTION_LEVEL_NETWORK, 'connectivity_test' => self::OPTION_LEVEL_NETWORK, 'handle_gdpr_admin_notice' => self::OPTION_LEVEL_NETWORK, 'has_trial_plan' => self::OPTION_LEVEL_NETWORK, 'install_sync_timestamp' => self::OPTION_LEVEL_NETWORK, 'install_sync_cron' => self::OPTION_LEVEL_NETWORK, 'is_anonymous_ms' => self::OPTION_LEVEL_NETWORK, 'is_network_activated' => self::OPTION_LEVEL_NETWORK, 'is_on' => self::OPTION_LEVEL_NETWORK, 'is_plugin_new_install' => self::OPTION_LEVEL_NETWORK, 'last_load_timestamp' => self::OPTION_LEVEL_NETWORK, 'network_install_blog_id' => self::OPTION_LEVEL_NETWORK, 'pending_sites_info' => self::OPTION_LEVEL_NETWORK, 'plugin_last_version' => self::OPTION_LEVEL_NETWORK, 'plugin_main_file' => self::OPTION_LEVEL_NETWORK, 'plugin_version' => self::OPTION_LEVEL_NETWORK, 'sdk_downgrade_mode' => self::OPTION_LEVEL_NETWORK, 'sdk_last_version' => self::OPTION_LEVEL_NETWORK, 'sdk_upgrade_mode' => self::OPTION_LEVEL_NETWORK, 'sdk_version' => self::OPTION_LEVEL_NETWORK, 'sticky_optin_added_ms' => self::OPTION_LEVEL_NETWORK, 'subscriptions' => self::OPTION_LEVEL_NETWORK, 'sync_timestamp' => self::OPTION_LEVEL_NETWORK, 'sync_cron' => self::OPTION_LEVEL_NETWORK, 'was_plugin_loaded' => self::OPTION_LEVEL_NETWORK, 'network_user_id' => self::OPTION_LEVEL_NETWORK, 'plugin_upgrade_mode' => self::OPTION_LEVEL_NETWORK, 'plugin_downgrade_mode' => self::OPTION_LEVEL_NETWORK, 'is_network_connected' => self::OPTION_LEVEL_NETWORK, /** * Special flag that is used when a super-admin upgrades to the new version of the SDK that supports network level integration, when the connection decision wasn't made for all the sites in the network. */ 'is_network_activation' => self::OPTION_LEVEL_NETWORK, 'license_migration' => self::OPTION_LEVEL_NETWORK, // When network activated, then network level. 'install_timestamp' => self::OPTION_LEVEL_NETWORK_ACTIVATED, 'prev_is_premium' => self::OPTION_LEVEL_NETWORK_ACTIVATED, 'require_license_activation' => self::OPTION_LEVEL_NETWORK_ACTIVATED, // If not network activated OR delegated, then site level. 'activation_timestamp' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'expired_license_notice_shown' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'is_whitelabeled' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'last_license_key' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'last_license_user_id' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'prev_user_id' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'sticky_optin_added' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'uninstall_reason' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'is_pending_activation' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, 'pending_license_key' => self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED, // Site level options. 'is_anonymous' => self::OPTION_LEVEL_SITE, 'clone_id' => self::OPTION_LEVEL_SITE, ); } /** * This method will and should only be executed when is_multisite() is true. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $key * @param int $option_level Since 2.5.1 * * @return bool */ private function is_multisite_option( $key, $option_level = self::OPTION_LEVEL_UNDEFINED ) { if ( ! isset( self::$_NETWORK_OPTIONS_MAP ) ) { self::load_network_options_map(); } if ( self::OPTION_LEVEL_UNDEFINED === $option_level && isset( self::$_NETWORK_OPTIONS_MAP[ $key ] ) ) { $option_level = self::$_NETWORK_OPTIONS_MAP[ $key ]; } if ( self::OPTION_LEVEL_UNDEFINED === $option_level ) { // Option not found -> use site level storage. return false; } if ( self::OPTION_LEVEL_NETWORK === $option_level ) { // Option found and set to always use the network level storage on a multisite. return true; } if ( self::OPTION_LEVEL_SITE === $option_level ) { // Option found and set to always use the site level storage on a multisite. return false; } if ( ! $this->_is_network_active ) { return false; } if ( self::OPTION_LEVEL_NETWORK_ACTIVATED === $option_level ) { // Network activated. return true; } if ( self::OPTION_LEVEL_NETWORK_ACTIVATED_NOT_DELEGATED === $option_level && ! $this->_is_delegated_connection ) { // Network activated and not delegated. return true; } return false; } /** * @author Leo Fajardo * * @param string $key * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_BINARY_MAP). * @param int $option_level Since 2.5.1 * * @return bool */ private function should_use_network_storage( $key, $network_level_or_blog_id = null, $option_level = self::OPTION_LEVEL_UNDEFINED ) { if ( ! $this->_is_multisite ) { // Not a multisite environment. return false; } if ( is_numeric( $network_level_or_blog_id ) ) { // Explicitly asked to use a specified blog storage. return false; } if ( is_bool( $network_level_or_blog_id ) ) { // Explicitly specified whether it should use the network or blog level storage. return $network_level_or_blog_id; } // Determine which storage to use based on the option. return $this->is_multisite_option( $key, $option_level ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id * * @return \FS_Key_Value_Storage */ private function get_site_storage( $blog_id = 0 ) { if ( ! is_numeric( $blog_id ) || $blog_id == $this->_blog_id || 0 == $blog_id ) { return $this->_storage; } return FS_Key_Value_Storage::instance( $this->_module_type . '_data', $this->_storage->get_secondary_id(), $blog_id ); } #endregion #-------------------------------------------------------------------------------- #region Magic methods #-------------------------------------------------------------------------------- function __set( $k, $v ) { if ( $this->should_use_network_storage( $k ) ) { $this->_network_storage->{$k} = $v; } else { $this->_storage->{$k} = $v; } } function __isset( $k ) { return $this->should_use_network_storage( $k ) ? isset( $this->_network_storage->{$k} ) : isset( $this->_storage->{$k} ); } function __unset( $k ) { if ( $this->should_use_network_storage( $k ) ) { unset( $this->_network_storage->{$k} ); } else { unset( $this->_storage->{$k} ); } } function __get( $k ) { return $this->should_use_network_storage( $k ) ? $this->_network_storage->{$k} : $this->_storage->{$k}; } #endregion } freemius/includes/class-fs-security.php000064400000004263147600046700014273 0ustar00id . $entity->secret_key . $entity->public_key . $action ); } /** * @param \FS_Scope_Entity $entity * @param int|bool $timestamp * @param string $action * * @return array */ function get_context_params( FS_Scope_Entity $entity, $timestamp = false, $action = '' ) { if ( false === $timestamp ) { $timestamp = time(); } return array( 's_ctx_type' => $entity->get_type(), 's_ctx_id' => $entity->id, 's_ctx_ts' => $timestamp, 's_ctx_secure' => $this->get_secure_token( $entity, $timestamp, $action ), ); } /** * Gets a sandbox trial token for a given plugin, plan, and trial timestamp. * * @param FS_Plugin $plugin * @param FS_Plugin_Plan $plan * @param int $trial_timestamp * * @return string */ function get_trial_token( FS_Plugin $plugin, FS_Plugin_Plan $plan, $trial_timestamp ) { return md5( $plugin->secret_key . $plugin->public_key . $plan->trial_period . $plan->id . $trial_timestamp ); } } freemius/includes/class-fs-plugin-updater.php000064400000176462147600046700015377 0ustar00get_id(); if ( ! isset( self::$_INSTANCES[ $key ] ) ) { self::$_INSTANCES[ $key ] = new self( $freemius ); } return self::$_INSTANCES[ $key ]; } #endregion private function __construct( Freemius $freemius ) { $this->_fs = $freemius; $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $freemius->get_slug() . '_updater', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); $this->filters(); } /** * Initiate required filters. * * @author Vova Feldman (@svovaf) * @since 1.0.4 */ private function filters() { // Override request for plugin information add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 ); $this->add_transient_filters(); /** * If user has the premium plugin's code but do NOT have an active license, * encourage him to upgrade by showing that there's a new release, but instead * of showing an update link, show upgrade link to the pricing page. * * @since 1.1.6 * */ // WP 2.9+ add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array( &$this, 'catch_plugin_update_row' ), 9 ); add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array( &$this, 'edit_and_echo_plugin_update_row' ), 11, 2 ); if ( ! $this->_fs->has_any_active_valid_license() ) { add_action( 'admin_head', array( &$this, 'catch_plugin_information_dialog_contents' ) ); } else { add_action( 'admin_footer', array( &$this, '_add_fs_allow_updater_and_dialog_request_param' ) ); } if ( ! WP_FS__IS_PRODUCTION_MODE ) { add_filter( 'http_request_host_is_external', array( $this, 'http_request_host_is_external_filter' ), 10, 3 ); } if ( $this->_fs->is_premium() ) { if ( ! $this->is_correct_folder_name() ) { add_filter( 'upgrader_post_install', array( &$this, '_maybe_update_folder_name' ), 10, 3 ); } add_filter( 'upgrader_pre_install', array( 'FS_Plugin_Updater', '_store_basename_for_source_adjustment' ), 1, 2 ); add_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1, 3 ); if ( ! $this->_fs->has_any_active_valid_license() ) { add_filter( 'wp_prepare_themes_for_js', array( &$this, 'change_theme_update_info_html' ), 10, 1 ); } } } /** * @author Leo Fajardo (@leorw) * @since 2.7.4 */ function _add_fs_allow_updater_and_dialog_request_param() { if ( ! $this->is_plugin_information_dialog_for_plugin() ) { return; } ?> _fs->get_slug() === fs_request_get_raw( 'plugin', false ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.1.4 */ function catch_plugin_information_dialog_contents() { if ( ! $this->is_plugin_information_dialog_for_plugin() ) { return; } add_action( 'admin_footer', array( &$this, 'edit_and_echo_plugin_information_dialog_contents' ), 0, 1 ); ob_start(); } /** * @author Leo Fajardo (@leorw) * @since 2.1.4 * * @param string $hook_suffix */ function edit_and_echo_plugin_information_dialog_contents( $hook_suffix ) { if ( 'plugin-information' !== fs_request_get( 'tab', false ) || $this->_fs->get_slug() !== fs_request_get_raw( 'plugin', false ) ) { return; } $license = $this->_fs->_get_license(); $subscription = ( is_object( $license ) && ! $license->is_lifetime() ) ? $this->_fs->_get_subscription( $license->id ) : null; $contents = ob_get_clean(); $install_or_update_button_id_attribute_pos = strpos( $contents, 'id="plugin_install_from_iframe"' ); if ( false === $install_or_update_button_id_attribute_pos ) { $install_or_update_button_id_attribute_pos = strpos( $contents, 'id="plugin_update_from_iframe"' ); } if ( false !== $install_or_update_button_id_attribute_pos ) { $install_or_update_button_start_pos = strrpos( substr( $contents, 0, $install_or_update_button_id_attribute_pos ), '', $install_or_update_button_id_attribute_pos ) + strlen( '' ) ); /** * The part of the contents without the update button. * * @author Leo Fajardo (@leorw) * @since 2.2.5 */ $modified_contents = substr( $contents, 0, $install_or_update_button_start_pos ); $install_or_update_button = substr( $contents, $install_or_update_button_start_pos, ( $install_or_update_button_end_pos - $install_or_update_button_start_pos ) ); /** * Replace the plugin information dialog's "Install Update Now" button's text and URL. If there's a license, * the text will be "Renew license" and will link to the checkout page with the license's billing cycle * and quota. If there's no license, the text will be "Buy license" and will link to the pricing page. */ $install_or_update_button = preg_replace( '/(\)(.+)(\<\/a>)/is', is_object( $license ) ? sprintf( '$1$4%s$6%s$8', $this->_fs->checkout_url( is_object( $subscription ) ? ( 1 == $subscription->billing_cycle ? WP_FS__PERIOD_MONTHLY : WP_FS__PERIOD_ANNUALLY ) : WP_FS__PERIOD_LIFETIME, false, array( 'licenses' => $license->quota ) ), fs_text_inline( 'Renew license', 'renew-license', $this->_fs->get_slug() ) ) : sprintf( '$1$4%s$6%s$8', $this->_fs->pricing_url(), fs_text_inline( 'Buy license', 'buy-license', $this->_fs->get_slug() ) ), $install_or_update_button ); /** * Append the modified button. * * @author Leo Fajardo (@leorw) * @since 2.2.5 */ $modified_contents .= $install_or_update_button; /** * Append the remaining part of the contents after the update button. * * @author Leo Fajardo (@leorw) * @since 2.2.5 */ $modified_contents .= substr( $contents, $install_or_update_button_end_pos ); $contents = $modified_contents; } echo $contents; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 */ private function add_transient_filters() { if ( $this->_fs->is_premium() && $this->_fs->is_registered() && ! FS_Permission_Manager::instance( $this->_fs )->is_essentials_tracking_allowed() ) { $this->_logger->log( 'Opted out sites cannot receive automatic software updates.' ); return; } add_filter( 'pre_set_site_transient_update_plugins', array( &$this, 'pre_set_site_transient_update_plugins_filter' ) ); add_filter( 'pre_set_site_transient_update_themes', array( &$this, 'pre_set_site_transient_update_plugins_filter' ) ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 */ private function remove_transient_filters() { remove_filter( 'pre_set_site_transient_update_plugins', array( &$this, 'pre_set_site_transient_update_plugins_filter' ) ); remove_filter( 'pre_set_site_transient_update_themes', array( &$this, 'pre_set_site_transient_update_plugins_filter' ) ); } /** * Capture plugin update row by turning output buffering. * * @author Vova Feldman (@svovaf) * @since 1.1.6 */ function catch_plugin_update_row() { ob_start(); } /** * Overrides default update message format with "renew your license" message. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $file * @param array $plugin_data */ function edit_and_echo_plugin_update_row( $file, $plugin_data ) { $plugin_update_row = ob_get_clean(); $current = get_site_transient( 'update_plugins' ); if ( ! isset( $current->response[ $file ] ) ) { echo $plugin_update_row; return; } $r = $current->response[ $file ]; $has_beta_update = $this->_fs->has_beta_update(); if ( $this->_fs->has_any_active_valid_license() ) { if ( $has_beta_update ) { /** * Turn the "new version" text into "new Beta version". * * Sample input: * There is a new version of Awesome Plugin available. update now. * Output: * There is a new Beta version of Awesome Plugin available. update now. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $plugin_update_row = preg_replace( '/(\)(.+)(\.+\)/is', ( '$1' . sprintf( fs_text_inline( 'There is a %s of %s available.', 'new-version-available', $this->_fs->get_slug() ), $has_beta_update ? fs_text_inline( 'new Beta version', 'new-beta-version', $this->_fs->get_slug() ) : fs_text_inline( 'new version', 'new-version', $this->_fs->get_slug() ), $this->_fs->get_plugin_title() ) . ' ' . '$3' . '$6' ), $plugin_update_row ); } } else { /** * Turn the "new version" text into a link that opens the plugin information dialog when clicked and * make the "View version x details" text link to the checkout page instead of opening the plugin * information dialog when clicked. * * Sample input: * There is a new version of Awesome Plugin available. update now. * Output: * There is a Buy a license now to access version x.y.z security & feature updates, and support. * OR * There is a Buy a license now to access version x.y.z security & feature updates, and support. * * @author Leo Fajardo (@leorw) */ $plugin_update_row = preg_replace( '/(\)(.+)(\.+\)/is', ( '$1' . sprintf( fs_text_inline( 'There is a %s of %s available.', 'new-version-available', $this->_fs->get_slug() ), sprintf( '%s', '$5', $has_beta_update ? fs_text_inline( 'new Beta version', 'new-beta-version', $this->_fs->get_slug() ) : fs_text_inline( 'new version', 'new-version', $this->_fs->get_slug() ) ), $this->_fs->get_plugin_title() ) . ' ' . $this->_fs->version_upgrade_checkout_link( $r->new_version ) . '$6' ), $plugin_update_row ); } if ( $this->_fs->is_plugin() && isset( $r->upgrade_notice ) && strlen( trim( $r->upgrade_notice ) ) > 0 ) { $slug = $this->_fs->get_slug(); $upgrade_notice_html = sprintf( '

    %3$s %4$s

    ', $slug, $this->_fs->get_module_type(), fs_text_inline( 'Important Upgrade Notice:', 'upgrade_notice', $slug ), esc_html( $r->upgrade_notice ) ); $plugin_update_row = str_replace( '', '' . $upgrade_notice_html, $plugin_update_row ); } echo $plugin_update_row; } /** * @author Leo Fajardo (@leorw) * @since 2.0.2 * * @param array $prepared_themes * * @return array */ function change_theme_update_info_html( $prepared_themes ) { $theme_basename = $this->_fs->get_plugin_basename(); if ( ! isset( $prepared_themes[ $theme_basename ] ) ) { return $prepared_themes; } $themes_update = get_site_transient( 'update_themes' ); if ( ! isset( $themes_update->response[ $theme_basename ] ) || empty( $themes_update->response[ $theme_basename ]['package'] ) ) { return $prepared_themes; } $prepared_themes[ $theme_basename ]['update'] = preg_replace( '/(\)(.+)(\)/is', '$1 $2 ' . $this->_fs->version_upgrade_checkout_link( $themes_update->response[ $theme_basename ]['new_version'] ) . '$4', $prepared_themes[ $theme_basename ]['update'] ); // Set to false to prevent the "Update now" link for the context theme from being shown on the "Themes" page. $prepared_themes[ $theme_basename ]['hasPackage'] = false; return $prepared_themes; } /** * Since WP version 3.6, a new security feature was added that denies access to repository with a local ip. * During development mode we want to be able updating plugin versions via our localhost repository. This * filter white-list all domains including "api.freemius". * * @link http://www.emanueletessore.com/wordpress-download-failed-valid-url-provided/ * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param bool $allow * @param string $host * @param string $url * * @return bool */ function http_request_host_is_external_filter( $allow, $host, $url ) { return ( false !== strpos( $host, 'freemius' ) ) ? true : $allow; } /** * Check for Updates at the defined API endpoint and modify the update array. * * This function dives into the update api just when WordPress creates its update array, * then adds a custom API call and injects the custom plugin data retrieved from the API. * It is reassembled from parts of the native WordPress plugin update code. * See wp-includes/update.php line 121 for the original wp_update_plugins() function. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @uses FS_Api * * @param object $transient_data Update array build by WordPress. * * @return object Modified update array with custom plugin data. */ function pre_set_site_transient_update_plugins_filter( $transient_data ) { $this->_logger->entrance(); /** * "plugins" or "themes". * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ $module_type = $this->_fs->get_module_type() . 's'; /** * Ensure that we don't mix plugins update info with themes update info. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ if ( "pre_set_site_transient_update_{$module_type}" !== current_filter() ) { return $transient_data; } if ( empty( $transient_data ) || defined( 'WP_FS__UNINSTALL_MODE' ) ) { return $transient_data; } global $wp_current_filter; $current_plugin_version = $this->_fs->get_plugin_version(); if ( ! empty( $wp_current_filter ) && 'upgrader_process_complete' === $wp_current_filter[0] ) { if ( is_null( $this->_update_details ) || ( is_object( $this->_update_details ) && $this->_update_details->new_version !== $current_plugin_version ) ) { /** * After an update, clear the stored update details and reparse the plugin's main file in order to get * the updated version's information and prevent the previous update information from showing up on the * updates page. * * @author Leo Fajardo (@leorw) * @since 2.3.1 */ $this->_update_details = null; $current_plugin_version = $this->_fs->get_plugin_version( true ); } } if ( ! isset( $this->_update_details ) ) { // Get plugin's newest update. $new_version = $this->_fs->get_update( false, fs_request_get_bool( 'force-check' ), FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION, $current_plugin_version ); $this->_update_details = false; if ( is_object( $new_version ) && $this->is_new_version_premium( $new_version ) ) { $this->_logger->log( 'Found newer plugin version ' . $new_version->version ); /** * Cache plugin details locally since set_site_transient( 'update_plugins' ) * called multiple times and the non wp.org plugins are filtered after the * call to .org. * * @since 1.1.8.1 */ $this->_update_details = $this->get_update_details( $new_version ); } } // Alias. $basename = $this->_fs->premium_plugin_basename(); if ( is_object( $this->_update_details ) ) { if ( isset( $transient_data->no_update ) ) { unset( $transient_data->no_update[ $basename ] ); } if ( ! isset( $transient_data->response ) ) { $transient_data->response = array(); } // Add plugin to transient data. $transient_data->response[ $basename ] = $this->_fs->is_plugin() ? $this->_update_details : (array) $this->_update_details; } else { if ( isset( $transient_data->response ) ) { /** * Ensure that there's no update data for the plugin to prevent upgrading the premium version to the latest free version. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ unset( $transient_data->response[ $basename ] ); } if ( ! isset( $transient_data->no_update ) ) { $transient_data->no_update = array(); } /** * Add product to no_update transient data to properly integrate with WP 5.5 auto-updates UI. * * @since 2.4.1 * @link https://make.wordpress.org/core/2020/07/30/recommended-usage-of-the-updates-api-to-support-the-auto-updates-ui-for-plugins-and-themes-in-wordpress-5-5/ */ $transient_data->no_update[ $basename ] = $this->_fs->is_plugin() ? (object) array( 'id' => $basename, 'slug' => $this->_fs->get_slug(), 'plugin' => $basename, 'new_version' => $this->_fs->get_plugin_version(), 'url' => '', 'package' => '', 'icons' => array(), 'banners' => array(), 'banners_rtl' => array(), 'tested' => '', 'requires_php' => '', 'compatibility' => new stdClass(), ) : array( 'theme' => $basename, 'new_version' => $this->_fs->get_plugin_version(), 'url' => '', 'package' => '', 'requires' => '', 'requires_php' => '', ); } $slug = $this->_fs->get_slug(); if ( $this->can_fetch_data_from_wp_org() ) { if ( ! isset( $this->_translation_updates ) ) { $this->_translation_updates = array(); $translation_updates = $this->fetch_wp_org_module_translation_updates( $module_type, $slug ); if ( ! empty( $translation_updates ) ) { $this->_translation_updates = $translation_updates; } } if ( ! empty( $this->_translation_updates ) ) { $all_translation_updates = ( isset( $transient_data->translations ) && is_array( $transient_data->translations ) ) ? $transient_data->translations : array(); $current_plugin_translation_updates_map = array(); foreach ( $all_translation_updates as $key => $translation_update ) { if ( $module_type === ( $translation_update['type'] . 's' ) && $slug === $translation_update['slug'] ) { $current_plugin_translation_updates_map[ $translation_update['language'] ] = $translation_update; unset( $all_translation_updates[ $key ] ); } } foreach ( $this->_translation_updates as $translation_update ) { $lang = $translation_update['language']; if ( ! isset( $current_plugin_translation_updates_map[ $lang ] ) || version_compare( $translation_update['version'], $current_plugin_translation_updates_map[ $lang ]['version'], '>' ) ) { $current_plugin_translation_updates_map[ $lang ] = $translation_update; } } $transient_data->translations = array_merge( $all_translation_updates, array_values( $current_plugin_translation_updates_map ) ); } } return $transient_data; } /** * Get module's required data for the updates' mechanism. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_Plugin_Tag $new_version * * @return object */ function get_update_details( FS_Plugin_Tag $new_version ) { $update = new stdClass(); $update->slug = $this->_fs->get_slug(); $update->new_version = $new_version->version; $update->url = WP_FS__ADDRESS; $update->package = $new_version->url; $update->tested = self::get_tested_wp_version( $new_version->tested_up_to_version ); $update->requires = $new_version->requires_platform_version; $update->requires_php = $new_version->requires_programming_language_version; $icon = $this->_fs->get_local_icon_url(); if ( ! empty( $icon ) ) { $update->icons = array( // '1x' => $icon, // '2x' => $icon, 'default' => $icon, ); } if ( $this->_fs->is_premium() ) { $latest_tag = $this->_fs->_fetch_latest_version( $this->_fs->get_id(), false ); if ( isset( $latest_tag->readme ) && isset( $latest_tag->readme->upgrade_notice ) && ! empty( $latest_tag->readme->upgrade_notice ) ) { $update->upgrade_notice = $latest_tag->readme->upgrade_notice; } } $update->{$this->_fs->get_module_type()} = $this->_fs->get_plugin_basename(); return $update; } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param FS_Plugin_Tag $new_version * * @return bool */ private function is_new_version_premium( FS_Plugin_Tag $new_version ) { $params = fs_parse_url_params( $new_version->url ); return ( isset( $params['is_premium'] ) && 'true' == $params['is_premium'] ); } /** * Update the updates transient with the module's update information. * * This method is required for multisite environment. * If a module is site activated (not network) and not on the main site, * the module will NOT be executed on the network level, therefore, the * custom updates logic will not be executed as well, so unless we force * the injection of the update into the updates transient, premium updates * will not work. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_Plugin_Tag $new_version */ function set_update_data( FS_Plugin_Tag $new_version ) { $this->_logger->entrance(); if ( ! $this->is_new_version_premium( $new_version ) ) { return; } $transient_key = "update_{$this->_fs->get_module_type()}s"; $transient_data = get_site_transient( $transient_key ); $transient_data = is_object( $transient_data ) ? $transient_data : new stdClass(); // Alias. $basename = $this->_fs->get_plugin_basename(); $is_plugin = $this->_fs->is_plugin(); if ( ! isset( $transient_data->response ) || ! is_array( $transient_data->response ) ) { $transient_data->response = array(); } else if ( ! empty( $transient_data->response[ $basename ] ) ) { $version = $is_plugin ? ( ! empty( $transient_data->response[ $basename ]->new_version ) ? $transient_data->response[ $basename ]->new_version : null ) : ( ! empty( $transient_data->response[ $basename ]['new_version'] ) ? $transient_data->response[ $basename ]['new_version'] : null ); if ( $version == $new_version->version ) { // The update data is already set. return; } } // Remove the added filters. $this->remove_transient_filters(); $this->_update_details = $this->get_update_details( $new_version ); // Set update data in transient. $transient_data->response[ $basename ] = $is_plugin ? $this->_update_details : (array) $this->_update_details; if ( ! isset( $transient_data->checked ) || ! is_array( $transient_data->checked ) ) { $transient_data->checked = array(); } // Flag the module as if it was already checked. $transient_data->checked[ $basename ] = $this->_fs->get_plugin_version(); $transient_data->last_checked = time(); set_site_transient( $transient_key, $transient_data ); $this->add_transient_filters(); } /** * @author Leo Fajardo (@leorw) * @since 2.0.2 */ function delete_update_data() { $this->_logger->entrance(); $transient_key = "update_{$this->_fs->get_module_type()}s"; $transient_data = get_site_transient( $transient_key ); // Alias $basename = $this->_fs->get_plugin_basename(); if ( ! is_object( $transient_data ) || ! isset( $transient_data->response ) || ! is_array( $transient_data->response ) || empty( $transient_data->response[ $basename ] ) ) { return; } unset( $transient_data->response[ $basename ] ); // Remove the added filters. $this->remove_transient_filters(); set_site_transient( $transient_key, $transient_data ); $this->add_transient_filters(); } /** * Try to fetch plugin's info from .org repository. * * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param string $action * @param object $args * * @return bool|mixed */ static function _fetch_plugin_info_from_repository( $action, $args ) { $url = $http_url = 'http://api.wordpress.org/plugins/info/1.2/'; $url = add_query_arg( array( 'action' => $action, 'request' => $args, ), $url ); if ( wp_http_supports( array( 'ssl' ) ) ) { $url = set_url_scheme( $url, 'https' ); } // The new endpoint version serves only GET requests. $request = wp_remote_get( $url, array( 'timeout' => 15 ) ); if ( is_wp_error( $request ) ) { return false; } $res = json_decode( wp_remote_retrieve_body( $request ), true ); if ( is_array( $res ) ) { // Object casting is required in order to match the info/1.0 format. We are not decoding directly into an object as we need some fields to remain an array (e.g., $res->sections). $res = (object) $res; } if ( ! is_object( $res ) || isset( $res->error ) ) { return false; } return $res; } /** * Returns true if the product can fetch data from WordPress.org. * * @author Leo Fajardo (@leorw) * @since 2.7.4 */ private function can_fetch_data_from_wp_org() { return ( $this->_fs->is_org_repo_compliant() && $this->_fs->is_freemium() ); } /** * Fetches module translation updates from wordpress.org. * * @author Leo Fajardo (@leorw) * @since 2.1.2 * * @param string $module_type * @param string $slug * * @return array|null */ private function fetch_wp_org_module_translation_updates( $module_type, $slug ) { $plugin_data = $this->_fs->get_plugin_data(); $locales = array_values( get_available_languages() ); $locales = apply_filters( "{$module_type}_update_check_locales", $locales ); $locales = array_unique( $locales ); $plugin_basename = $this->_fs->get_plugin_basename(); if ( 'themes' === $module_type ) { $plugin_basename = $slug; } global $wp_version; $request_args = array( 'timeout' => 15, 'body' => array( "{$module_type}" => json_encode( array( "{$module_type}" => array( $plugin_basename => array( 'Name' => trim( str_replace( $this->_fs->get_plugin()->premium_suffix, '', $plugin_data['Name'] ) ), 'Author' => $plugin_data['Author'], ) ) ) ), 'translations' => json_encode( $this->get_installed_translations( $module_type, $slug ) ), 'locale' => json_encode( $locales ) ), 'user-agent' => ( 'WordPress/' . $wp_version . '; ' . home_url( '/' ) ) ); $url = "http://api.wordpress.org/{$module_type}/update-check/1.1/"; if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) { $url = set_url_scheme( $url, 'https' ); } $raw_response = Freemius::safe_remote_post( $url, $request_args, WP_FS__TIME_24_HOURS_IN_SEC, WP_FS__TIME_12_HOURS_IN_SEC, false ); if ( is_wp_error( $raw_response ) ) { return null; } $response = json_decode( wp_remote_retrieve_body( $raw_response ), true ); if ( ! is_array( $response ) ) { return null; } if ( ! isset( $response['translations'] ) || empty( $response['translations'] ) ) { return null; } return $response['translations']; } /** * @author Leo Fajardo (@leorw) * @since 2.1.2 * * @param string $module_type * @param string $slug * * @return array */ private function get_installed_translations( $module_type, $slug ) { if ( function_exists( 'wp_get_installed_translations' ) ) { return wp_get_installed_translations( $module_type ); } $dir = "/{$module_type}"; if ( ! is_dir( WP_LANG_DIR . $dir ) ) return array(); $files = scandir( WP_LANG_DIR . $dir ); if ( ! $files ) return array(); $language_data = array(); foreach ( $files as $file ) { if ( 0 !== strpos( $file, $slug ) ) { continue; } if ( '.' === $file[0] || is_dir( WP_LANG_DIR . "{$dir}/{$file}" ) ) { continue; } if ( substr( $file, -3 ) !== '.po' ) { continue; } if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) { continue; } if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files ) ) { continue; } list( , $textdomain, $language ) = $match; if ( '' === $textdomain ) { $textdomain = 'default'; } $language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "{$dir}/{$file}" ); } return $language_data; } /** * Updates information on the "View version x.x details" page with custom data. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @uses FS_Api * * @param object $data * @param string $action * @param mixed $args * * @return object */ function plugins_api_filter( $data, $action = '', $args = null ) { $this->_logger->entrance(); if ( ( 'plugin_information' !== $action ) || ! isset( $args->slug ) ) { return $data; } $addon = false; $is_addon = false; $addon_version = false; if ( $this->_fs->get_slug() !== $args->slug ) { $addon = $this->_fs->get_addon_by_slug( $args->slug ); if ( ! is_object( $addon ) ) { return $data; } if ( $this->_fs->is_addon_activated( $addon->id ) ) { $addon_version = $this->_fs->get_addon_instance( $addon->id )->get_plugin_version(); } else if ( $this->_fs->is_addon_installed( $addon->id ) ) { $addon_plugin_data = get_plugin_data( ( WP_PLUGIN_DIR . '/' . $this->_fs->get_addon_basename( $addon->id ) ), false, false ); if ( ! empty( $addon_plugin_data ) ) { $addon_version = $addon_plugin_data['Version']; } } $is_addon = true; } $plugin_in_repo = false; if ( ! $is_addon && $this->can_fetch_data_from_wp_org() ) { // Try to fetch info from .org repository. $data = self::_fetch_plugin_info_from_repository( $action, $args ); $plugin_in_repo = ( false !== $data ); } if ( ! $plugin_in_repo ) { $data = $args; // Fetch as much as possible info from local files. $plugin_local_data = $this->_fs->get_plugin_data(); $data->name = $plugin_local_data['Name']; $data->author = $plugin_local_data['Author']; $data->sections = array( 'description' => 'Upgrade ' . $plugin_local_data['Name'] . ' to latest.', ); // @todo Store extra plugin info on Freemius or parse readme.txt markup. /*$info = $this->_fs->get_api_site_scope()->call('/information.json'); if ( !isset($info->error) ) { $data = $info; }*/ } $plugin_version = $is_addon ? $addon_version : $this->_fs->get_plugin_version(); // Get plugin's newest update. $new_version = $this->get_latest_download_details( $is_addon ? $addon->id : false, $plugin_version ); if ( ! is_object( $new_version ) || empty( $new_version->version ) ) { $data->version = $plugin_version; } else { if ( $is_addon ) { $data->name = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ); $data->slug = $addon->slug; $data->url = WP_FS__ADDRESS; $data->package = $new_version->url; } if ( ! $plugin_in_repo ) { $data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created; $data->requires = $new_version->requires_platform_version; $data->requires_php = $new_version->requires_programming_language_version; $data->tested = $new_version->tested_up_to_version; } $data->version = $new_version->version; $data->download_link = $new_version->url; if ( isset( $new_version->readme ) && is_object( $new_version->readme ) ) { $new_version_readme_data = $new_version->readme; if ( isset( $new_version_readme_data->sections ) ) { $new_version_readme_data->sections = (array) $new_version_readme_data->sections; } else { $new_version_readme_data->sections = array(); } if ( isset( $data->sections ) ) { if ( isset( $data->sections['screenshots'] ) ) { $new_version_readme_data->sections['screenshots'] = $data->sections['screenshots']; } if ( isset( $data->sections['reviews'] ) ) { $new_version_readme_data->sections['reviews'] = $data->sections['reviews']; } } if ( isset( $new_version_readme_data->banners ) ) { $new_version_readme_data->banners = (array) $new_version_readme_data->banners; } else if ( isset( $data->banners ) ) { $new_version_readme_data->banners = $data->banners; } $wp_org_sections = array( 'author', 'author_profile', 'rating', 'ratings', 'num_ratings', 'support_threads', 'support_threads_resolved', 'active_installs', 'added', 'homepage' ); foreach ( $wp_org_sections as $wp_org_section ) { if ( isset( $data->{$wp_org_section} ) ) { $new_version_readme_data->{$wp_org_section} = $data->{$wp_org_section}; } } $data = $new_version_readme_data; } } if ( ! empty( $data->tested ) ) { $data->tested = self::get_tested_wp_version( $data->tested ); } return $data; } /** * @since 2.5.3 If the current WordPress version is a patch of the tested version (e.g., 6.1.2 is a patch of 6.1), then override the tested version with the patch so developers won't need to release a new version just to bump the latest supported WP version. * * @param string|null $tested_up_to * * @return string|null */ private static function get_tested_wp_version( $tested_up_to ) { $current_wp_version = get_bloginfo( 'version' ); return ( ! empty($tested_up_to) && fs_starts_with( $current_wp_version, $tested_up_to . '.' ) ) ? $current_wp_version : $tested_up_to; } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param number|bool $addon_id * @param bool|string $newer_than Since 2.2.1 * @param bool|string $fetch_readme Since 2.2.1 * * @return object */ private function get_latest_download_details( $addon_id = false, $newer_than = false, $fetch_readme = true ) { return $this->_fs->_fetch_latest_version( $addon_id, true, FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION, $newer_than, $fetch_readme ); } /** * Checks if a given basename has a matching folder name * with the current context plugin. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @return bool */ private function is_correct_folder_name() { return ( $this->_fs->get_target_folder_name() == trim( dirname( $this->_fs->get_plugin_basename() ), '/\\' ) ); } /** * This is a special after upgrade handler for migrating modules * that didn't use the '-premium' suffix folder structure before * the migration. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @param bool $response Install response. * @param array $hook_extra Extra arguments passed to hooked filters. * @param array $result Installation result data. * * @return bool */ function _maybe_update_folder_name( $response, $hook_extra, $result ) { $basename = $this->_fs->get_plugin_basename(); if ( true !== $response || empty( $hook_extra ) || empty( $hook_extra['plugin'] ) || $basename !== $hook_extra['plugin'] ) { return $response; } $active_plugins_basenames = get_option( 'active_plugins' ); foreach ( $active_plugins_basenames as $key => $active_plugin_basename ) { if ( $basename === $active_plugin_basename ) { // Get filename including extension. $filename = basename( $basename ); $new_basename = plugin_basename( trailingslashit( $this->_fs->is_premium() ? $this->_fs->get_premium_slug() : $this->_fs->get_slug() ) . $filename ); // Verify that the expected correct path exists. if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $new_basename ) ) ) { // Override active plugin name. $active_plugins_basenames[ $key ] = $new_basename; update_option( 'active_plugins', $active_plugins_basenames ); } break; } } return $response; } #---------------------------------------------------------------------------------- #region Auto Activation #---------------------------------------------------------------------------------- /** * Installs and active a plugin when explicitly requested that from a 3rd party service. * * This logic was inspired by the TGMPA GPL licensed library by Thomas Griffin. * * @link http://tgmpluginactivation.com/ * * @author Vova Feldman * @since 1.2.1.7 * * @link https://make.wordpress.org/plugins/2017/03/16/clarification-of-guideline-8-executable-code-and-installs/ * * @uses WP_Filesystem * @uses WP_Error * @uses WP_Upgrader * @uses Plugin_Upgrader * @uses Plugin_Installer_Skin * @uses Plugin_Upgrader_Skin * * @param number|bool $plugin_id * * @return array */ function install_and_activate_plugin( $plugin_id = false ) { if ( ! empty( $plugin_id ) && ! FS_Plugin::is_valid_id( $plugin_id ) ) { // Invalid plugin ID. return array( 'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ), 'code' => 'invalid_module_id', ); } $is_addon = false; if ( FS_Plugin::is_valid_id( $plugin_id ) && $plugin_id != $this->_fs->get_id() ) { $addon = $this->_fs->get_addon( $plugin_id ); if ( ! is_object( $addon ) ) { // Invalid add-on ID. return array( 'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ), 'code' => 'invalid_module_id', ); } $slug = $addon->slug; $premium_slug = $addon->premium_slug; $title = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ); $is_addon = true; } else { $slug = $this->_fs->get_slug(); $premium_slug = $this->_fs->get_premium_slug(); $title = $this->_fs->get_plugin_title() . ( $this->_fs->is_addon() ? ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ) : '' ); } if ( $this->is_premium_plugin_active( $plugin_id ) ) { // Premium version already activated. return array( 'message' => $is_addon ? $this->_fs->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ) : $this->_fs->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ), 'code' => 'premium_installed', ); } $latest_version = $this->get_latest_download_details( $plugin_id, false, false ); $target_folder = $premium_slug; // Prep variables for Plugin_Installer_Skin class. $extra = array(); $extra['slug'] = $target_folder; $source = $latest_version->url; $api = null; $install_url = add_query_arg( array( 'action' => 'install-plugin', 'plugin' => urlencode( $slug ), ), 'update.php' ); if ( ! class_exists( 'Plugin_Upgrader', false ) ) { // Include required resources for the installation. require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; } $skin_args = array( 'type' => 'web', 'title' => sprintf( $this->_fs->get_text_inline( 'Installing plugin: %s', 'installing-plugin-x' ), $title ), 'url' => esc_url_raw( $install_url ), 'nonce' => 'install-plugin_' . $slug, 'plugin' => '', 'api' => $api, 'extra' => $extra, ); // $skin = new Automatic_Upgrader_Skin( $skin_args ); // $skin = new Plugin_Installer_Skin( $skin_args ); $skin = new WP_Ajax_Upgrader_Skin( $skin_args ); // Create a new instance of Plugin_Upgrader. $upgrader = new Plugin_Upgrader( $skin ); // Perform the action and install the plugin from the $source urldecode(). add_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1, 3 ); $install_result = $upgrader->install( $source ); remove_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1 ); if ( is_wp_error( $install_result ) ) { return array( 'message' => $install_result->get_error_message(), 'code' => $install_result->get_error_code(), ); } elseif ( is_wp_error( $skin->result ) ) { return array( 'message' => $skin->result->get_error_message(), 'code' => $skin->result->get_error_code(), ); } elseif ( $skin->get_errors()->get_error_code() ) { return array( 'message' => $skin->get_error_messages(), 'code' => 'unknown', ); } elseif ( is_null( $install_result ) ) { global $wp_filesystem; $error_code = 'unable_to_connect_to_filesystem'; $error_message = $this->_fs->get_text_inline( 'Unable to connect to the filesystem. Please confirm your credentials.' ); // Pass through the error from WP_Filesystem if one was raised. if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) { $error_message = $wp_filesystem->errors->get_error_message(); } return array( 'message' => $error_message, 'code' => $error_code, ); } // Grab the full path to the main plugin's file. $plugin_activate = $upgrader->plugin_info(); // Try to activate the plugin. $activation_result = $this->try_activate_plugin( $plugin_activate ); if ( is_wp_error( $activation_result ) ) { return array( 'message' => $activation_result->get_error_message(), 'code' => $activation_result->get_error_code(), ); } return $skin->get_upgrade_messages(); } /** * Tries to activate a plugin. If fails, returns the error. * * @author Vova Feldman * @since 1.2.1.7 * * @param string $file_path Path within wp-plugins/ to main plugin file. * This determines the styling of the output messages. * * @return bool|WP_Error */ protected function try_activate_plugin( $file_path ) { $activate = activate_plugin( $file_path, '', $this->_fs->is_network_active() ); return is_wp_error( $activate ) ? $activate : true; } /** * Check if a premium module version is already active. * * @author Vova Feldman * @since 1.2.1.7 * * @param number|bool $plugin_id * * @return bool */ private function is_premium_plugin_active( $plugin_id = false ) { if ( $plugin_id != $this->_fs->get_id() ) { return $this->_fs->is_addon_activated( $plugin_id, true ); } return is_plugin_active( $this->_fs->premium_plugin_basename() ); } /** * Store the basename since it's not always available in the `_maybe_adjust_source_dir` method below. * * @author Leo Fajardo (@leorw) * @since 2.2.1 * * @param bool|WP_Error $response Response. * @param array $hook_extra Extra arguments passed to hooked filters. * * @return bool|WP_Error */ static function _store_basename_for_source_adjustment( $response, $hook_extra ) { if ( isset( $hook_extra['plugin'] ) ) { self::$_upgrade_basename = $hook_extra['plugin']; } else if ( isset( $hook_extra['theme'] ) ) { self::$_upgrade_basename = $hook_extra['theme']; } else { self::$_upgrade_basename = null; } return $response; } /** * Adjust the plugin directory name if necessary. * Assumes plugin has a folder (not a single file plugin). * * The final destination directory of a plugin is based on the subdirectory name found in the * (un)zipped source. In some cases this subdirectory name is not the same as the expected * slug and the plugin will not be recognized as installed. This is fixed by adjusting * the temporary unzipped source subdirectory name to the expected plugin slug. * * @author Vova Feldman * @since 1.2.1.7 * @since 2.2.1 The method was converted to static since when the admin update bulk products via the Updates section, the logic applies the `upgrader_source_selection` filter for every product that is being updated. * * @param string $source Path to upgrade/zip-file-name.tmp/subdirectory/. * @param string $remote_source Path to upgrade/zip-file-name.tmp. * @param \WP_Upgrader $upgrader Instance of the upgrader which installs the plugin. * * @return string|WP_Error */ static function _maybe_adjust_source_dir( $source, $remote_source, $upgrader ) { if ( ! is_object( $GLOBALS['wp_filesystem'] ) ) { return $source; } $basename = self::$_upgrade_basename; $is_theme = false; // Figure out what the slug is supposed to be. if ( isset( $upgrader->skin->options['extra'] ) ) { // Set by the auto-install logic. $desired_slug = $upgrader->skin->options['extra']['slug']; } else if ( ! empty( $basename ) ) { /** * If it doesn't end with ".php", it's a theme. * * @author Leo Fajardo (@leorw) * @since 2.2.1 */ $is_theme = ( ! fs_ends_with( $basename, '.php' ) ); $desired_slug = ( ! $is_theme ) ? dirname( $basename ) : // Theme slug $basename; } else { // Can't figure out the desired slug, stop the execution. return $source; } if ( is_multisite() ) { /** * If we are running in a multisite environment and the product is not network activated, * the instance will not exist anyway. Therefore, try to update the source if necessary * regardless if the Freemius instance of the product exists or not. * * @author Vova Feldman */ } else if ( ! empty( $basename ) ) { $fs = Freemius::get_instance_by_file( $basename, $is_theme ? WP_FS__MODULE_TYPE_THEME : WP_FS__MODULE_TYPE_PLUGIN ); if ( ! is_object( $fs ) ) { /** * If the Freemius instance does not exist on a non-multisite network environment, it means that: * 1. The product is not powered by Freemius; OR * 2. The product is not activated, therefore, we don't mind if after the update the folder name will change. * * @author Leo Fajardo (@leorw) * @since 2.2.1 */ return $source; } } $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) ); if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) { $from_path = untrailingslashit( $source ); $to_path = trailingslashit( $remote_source ) . $desired_slug; if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) { return trailingslashit( $to_path ); } return new WP_Error( 'rename_failed', fs_text_inline( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'module-package-rename-failure' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) ); } return $source; } #endregion } freemius/includes/class-fs-options.php000064400000040227147600046700014117 0ustar00_id = $id; $this->_is_multisite = is_multisite(); if ( $this->_is_multisite ) { $this->_blog_id = get_current_blog_id(); $this->_network_options = FS_Option_Manager::get_manager( $id, $load, true ); } $this->_options = FS_Option_Manager::get_manager( $id, $load, $this->_blog_id ); } /** * Switch the context of the site level options manager. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param $blog_id */ function set_site_blog_context( $blog_id ) { $this->_blog_id = $blog_id; $this->_options = FS_Option_Manager::get_manager( $this->_id, false, $this->_blog_id ); } /** * @author Leo Fajardo (@leorw) * * @param string $option * @param mixed $default * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS). * * @return mixed */ function get_option( $option, $default = null, $network_level_or_blog_id = null ) { if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) { return $this->_network_options->get_option( $option, $default ); } $site_options = $this->get_site_options( $network_level_or_blog_id ); return $site_options->get_option( $option, $default ); } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param string $option * @param mixed $value * @param bool $flush * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS). */ function set_option( $option, $value, $flush = false, $network_level_or_blog_id = null ) { if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) { $this->_network_options->set_option( $option, $value, $flush ); } else { $site_options = $this->get_site_options( $network_level_or_blog_id ); $site_options->set_option( $option, $value, $flush ); } } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $option * @param bool $flush * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS). */ function unset_option( $option, $flush = false, $network_level_or_blog_id = null ) { if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) { $this->_network_options->unset_option( $option, $flush ); } else { $site_options = $this->get_site_options( $network_level_or_blog_id ); $site_options->unset_option( $option, $flush ); } } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param bool $flush * @param bool $network_level */ function load( $flush = false, $network_level = true ) { if ( $this->_is_multisite && $network_level ) { $this->_network_options->load( $flush ); } else { $this->_options->load( $flush ); } } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, store both network storage and the current context blog storage. */ function store( $network_level_or_blog_id = null ) { if ( ! $this->_is_multisite || false === $network_level_or_blog_id || 0 == $network_level_or_blog_id || is_null( $network_level_or_blog_id ) ) { $site_options = $this->get_site_options( $network_level_or_blog_id ); $site_options->store(); } if ( $this->_is_multisite && ( is_null( $network_level_or_blog_id ) || true === $network_level_or_blog_id ) ) { $this->_network_options->store(); } } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int|null|bool $network_level_or_blog_id * @param bool $flush */ function clear( $network_level_or_blog_id = null, $flush = false ) { if ( ! $this->_is_multisite || false === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) || is_numeric( $network_level_or_blog_id ) ) { $site_options = $this->get_site_options( $network_level_or_blog_id ); $site_options->clear( $flush ); } if ( $this->_is_multisite && ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) ) ) { $this->_network_options->clear( $flush ); } } /** * Migration script to the new storage data structure that is network compatible. * * IMPORTANT: * This method should be executed only after it is determined if this is a network * level compatible product activation. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id */ function migrate_to_network( $blog_id = 0 ) { if ( ! $this->_is_multisite ) { return; } $updated = false; $site_options = $this->get_site_options( $blog_id ); $keys = $site_options->get_options_keys(); foreach ( $keys as $option ) { if ( $this->is_site_option( $option ) || // Don't move admin notices to the network storage. in_array($option, array( // Don't move admin notices to the network storage. 'admin_notices', // Don't migrate the module specific data, it will be migrated by the FS_Storage. 'plugin_data', 'theme_data', )) ) { continue; } $option_updated = false; // Migrate option to the network storage. $site_option = $site_options->get_option( $option ); if ( ! $this->_network_options->has_option( $option ) ) { // Option not set on the network level, so just set it. $this->_network_options->set_option( $option, $site_option, false ); $option_updated = true; } else { // Option already set on the network level, so we need to merge it inelegantly. $network_option = $this->_network_options->get_option( $option ); if ( is_array( $network_option ) && is_array( $site_option ) ) { // Option is an array. foreach ( $site_option as $key => $value ) { if ( ! isset( $network_option[ $key ] ) ) { $network_option[ $key ] = $value; $option_updated = true; } else if ( is_array( $network_option[ $key ] ) && is_array( $value ) ) { if ( empty( $network_option[ $key ] ) ) { $network_option[ $key ] = $value; $option_updated = true; } else if ( empty( $value ) ) { // Do nothing. } else { reset($value); $first_key = key($value); if ( $value[$first_key] instanceof FS_Entity ) { // Merge entities by IDs. $network_entities_ids = array(); foreach ( $network_option[ $key ] as $entity ) { $network_entities_ids[ $entity->id ] = true; } foreach ( $value as $entity ) { if ( ! isset( $network_entities_ids[ $entity->id ] ) ) { $network_option[ $key ][] = $entity; $option_updated = true; } } } } } } } if ( $option_updated ) { $this->_network_options->set_option( $option, $network_option, false ); } } /** * Remove the option from site level storage. * * IMPORTANT: * The line below is intentionally commented since we want to preserve the option * on the site storage level for "downgrade compatibility". Basically, if the user * will downgrade to an older version of the plugin with the prev storage structure, * it will continue working. * * @todo After a few releases we can remove this. */ // $site_options->unset_option($option, false); if ( $option_updated ) { $updated = true; } } if ( ! $updated ) { return; } // Update network level storage. $this->_network_options->store(); // $site_options->store(); } #-------------------------------------------------------------------------------- #region Helper Methods #-------------------------------------------------------------------------------- /** * We don't want to load the map right away since it's not even needed in a non-MS environment. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ private static function load_site_options_map() { self::$_SITE_OPTIONS_MAP = array( 'sites' => true, 'theme_sites' => true, 'unique_id' => true, 'active_plugins' => true, ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $option * * @return bool */ private function is_site_option( $option ) { if ( WP_FS__ACCOUNTS_OPTION_NAME != $this->_id ) { return false; } if ( ! isset( self::$_SITE_OPTIONS_MAP ) ) { self::load_site_options_map(); } return isset( self::$_SITE_OPTIONS_MAP[ $option ] ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id * * @return FS_Option_Manager */ private function get_site_options( $blog_id = 0 ) { if ( 0 == $blog_id || $blog_id == $this->_blog_id ) { return $this->_options; } return FS_Option_Manager::get_manager( $this->_id, true, $blog_id ); } /** * Check if an option should be stored on the MS network storage. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $option * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS). * * @return bool */ private function should_use_network_storage( $option, $network_level_or_blog_id = null ) { if ( ! $this->_is_multisite ) { // Not a multisite environment. return false; } if ( is_numeric( $network_level_or_blog_id ) ) { // Explicitly asked to use a specified blog storage. return false; } if ( is_bool( $network_level_or_blog_id ) ) { // Explicitly specified whether should use the network or blog level storage. return $network_level_or_blog_id; } // Determine which storage to use based on the option. return ! $this->is_site_option( $option ); } #endregion }freemius/includes/class-fs-logger.php000064400000041556147600046700013711 0ustar00_id = $id; $caller = $bt[2]; if ( false !== strpos( $caller['file'], 'plugins' ) ) { $this->_file_start = strpos( $caller['file'], 'plugins' ) + strlen( 'plugins/' ); } else { $this->_file_start = strpos( $caller['file'], 'themes' ) + strlen( 'themes/' ); } if ( $on ) { $this->on(); } if ( $echo ) { $this->echo_on(); } } /** * @param string $id * @param bool $on * @param bool $echo * * @return FS_Logger */ public static function get_logger( $id, $on = false, $echo = false ) { $id = strtolower( $id ); if ( ! isset( self::$_processID ) ) { self::init(); } if ( ! isset( self::$LOGGERS[ $id ] ) ) { self::$LOGGERS[ $id ] = new FS_Logger( $id, $on, $echo ); } return self::$LOGGERS[ $id ]; } /** * Initialize logging global info. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 */ private static function init() { self::$_ownerName = function_exists( 'get_current_user' ) ? get_current_user() : 'unknown'; self::$_isStorageLoggingOn = ( 1 == get_option( 'fs_storage_logger', 0 ) ); self::$_abspathLength = strlen( ABSPATH ); self::$_processID = mt_rand( 0, 32000 ); // Process ID may be `false` on errors. if ( ! is_numeric( self::$_processID ) ) { self::$_processID = 0; } } private static function hook_footer() { if ( self::$_HOOKED_FOOTER ) { return; } if ( is_admin() ) { add_action( 'admin_footer', 'FS_Logger::dump', 100 ); } else { add_action( 'wp_footer', 'FS_Logger::dump', 100 ); } } function is_on() { return $this->_on; } function on() { $this->_on = true; if ( ! function_exists( 'dbDelta' ) ) { require_once ABSPATH . 'wp-admin/includes/upgrade.php'; } self::hook_footer(); } function echo_on() { $this->on(); $this->_echo = true; } function is_echo_on() { return $this->_echo; } function get_id() { return $this->_id; } function get_file() { return $this->_file_start; } private function _log( &$message, $type, $wrapper = false ) { if ( ! $this->is_on() ) { return; } $bt = debug_backtrace(); $depth = $wrapper ? 3 : 2; while ( $depth < count( $bt ) - 1 && 'eval' === $bt[ $depth ]['function'] ) { $depth ++; } $caller = $bt[ $depth ]; /** * Retrieve the correct call file & line number from backtrace * when logging from a wrapper method. * * @author Vova Feldman * @since 1.2.1.6 */ if ( empty( $caller['line'] ) ) { $depth --; while ( $depth >= 0 ) { if ( ! empty( $bt[ $depth ]['line'] ) ) { $caller['line'] = $bt[ $depth ]['line']; $caller['file'] = $bt[ $depth ]['file']; break; } } } $log = array_merge( $caller, array( 'cnt' => self::$CNT ++, 'logger' => $this, 'timestamp' => microtime( true ), 'log_type' => $type, 'msg' => $message, ) ); if ( self::$_isStorageLoggingOn ) { $this->db_log( $type, $message, self::$CNT, $caller ); } self::$LOG[] = $log; if ( $this->is_echo_on() && ! Freemius::is_ajax() ) { echo self::format_html( $log ) . "\n"; } } function log( $message, $wrapper = false ) { $this->_log( $message, 'log', $wrapper ); } function info( $message, $wrapper = false ) { $this->_log( $message, 'info', $wrapper ); } function warn( $message, $wrapper = false ) { $this->_log( $message, 'warn', $wrapper ); } function error( $message, $wrapper = false ) { $this->_log( $message, 'error', $wrapper ); } /** * Log API error. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $api_result * @param bool $wrapper */ function api_error( $api_result, $wrapper = false ) { $message = ''; if ( is_object( $api_result ) && ! empty( $api_result->error ) && ! empty( $api_result->error->message ) ) { $message = $api_result->error->message; } else if ( is_object( $api_result ) ) { $message = var_export( $api_result, true ); } else if ( is_string( $api_result ) ) { $message = $api_result; } else if ( empty( $api_result ) ) { $message = 'Empty API result.'; } $message = 'API Error: ' . $message; $this->_log( $message, 'error', $wrapper ); } function entrance( $message = '', $wrapper = false ) { $msg = 'Entrance' . ( empty( $message ) ? '' : ' > ' ) . $message; $this->_log( $msg, 'log', $wrapper ); } function departure( $message = '', $wrapper = false ) { $msg = 'Departure' . ( empty( $message ) ? '' : ' > ' ) . $message; $this->_log( $msg, 'log', $wrapper ); } #-------------------------------------------------------------------------------- #region Log Formatting #-------------------------------------------------------------------------------- private static function format( $log, $show_type = true ) { return '[' . str_pad( $log['cnt'], strlen( self::$CNT ), '0', STR_PAD_LEFT ) . '] [' . $log['logger']->_id . '] ' . ( $show_type ? '[' . $log['log_type'] . ']' : '' ) . ( ! empty( $log['class'] ) ? $log['class'] . $log['type'] : '' ) . $log['function'] . ' >> ' . $log['msg'] . ( isset( $log['file'] ) ? ' (' . substr( $log['file'], $log['logger']->_file_start ) . ' ' . $log['line'] . ') ' : '' ) . ' [' . $log['timestamp'] . ']'; } private static function format_html( $log ) { return '
    [' . $log['cnt'] . '] [' . $log['logger']->_id . '] [' . $log['log_type'] . '] ' . ( ! empty( $log['class'] ) ? $log['class'] . $log['type'] : '' ) . $log['function'] . ' >> ' . esc_html( $log['msg'] ) . '' . ( isset( $log['file'] ) ? ' (' . substr( $log['file'], $log['logger']->_file_start ) . ' ' . $log['line'] . ')' : '' ) . ' [' . $log['timestamp'] . ']
    '; } #endregion static function dump() { ?> prefix}fs_logger"; /** * Drop logging table in any case. */ $result = $wpdb->query( "DROP TABLE IF EXISTS $table;" ); if ( $is_on ) { /** * Create logging table. * * NOTE: * dbDelta must use KEY and not INDEX for indexes. * * @link https://core.trac.wordpress.org/ticket/2695 */ $result = $wpdb->query( "CREATE TABLE IF NOT EXISTS {$table} ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `process_id` INT UNSIGNED NOT NULL, `user_name` VARCHAR(64) NOT NULL, `logger` VARCHAR(128) NOT NULL, `log_order` INT UNSIGNED NOT NULL, `type` ENUM('log','info','warn','error') NOT NULL DEFAULT 'log', `message` TEXT NOT NULL, `file` VARCHAR(256) NOT NULL, `line` INT UNSIGNED NOT NULL, `function` VARCHAR(256) NOT NULL, `request_type` ENUM('call','ajax','cron') NOT NULL DEFAULT 'call', `request_url` VARCHAR(1024) NOT NULL, `created` DECIMAL(16, 6) NOT NULL, PRIMARY KEY (`id`), KEY `process_id` (`process_id` ASC), KEY `process_logger` (`process_id` ASC, `logger` ASC), KEY `function` (`function` ASC), KEY `type` (`type` ASC))" ); } if ( false !== $result ) { update_option( 'fs_storage_logger', ( $is_on ? 1 : 0 ) ); self::$_isStorageLoggingOn = $is_on; } return ( false !== $result ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @param string $type * @param string $message * @param int $log_order * @param array $caller * * @return false|int */ private function db_log( &$type, &$message, &$log_order, &$caller ) { global $wpdb; $request_type = 'call'; if ( defined( 'DOING_CRON' ) && DOING_CRON ) { $request_type = 'cron'; } else if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { $request_type = 'ajax'; } $request_url = WP_FS__IS_HTTP_REQUEST ? $_SERVER['REQUEST_URI'] : ''; return $wpdb->insert( "{$wpdb->prefix}fs_logger", array( 'process_id' => self::$_processID, 'user_name' => self::$_ownerName, 'logger' => $this->_id, 'log_order' => $log_order, 'type' => $type, 'request_type' => $request_type, 'request_url' => $request_url, 'message' => $message, 'file' => isset( $caller['file'] ) ? substr( $caller['file'], self::$_abspathLength ) : '', 'line' => $caller['line'], 'function' => ( ! empty( $caller['class'] ) ? $caller['class'] . $caller['type'] : '' ) . $caller['function'], 'created' => microtime( true ), ) ); } /** * Persistent DB logger columns. * * @var array */ private static $_log_columns = array( 'id', 'process_id', 'user_name', 'logger', 'log_order', 'type', 'message', 'file', 'line', 'function', 'request_type', 'request_url', 'created', ); /** * Create DB logs query. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @param bool $filters * @param int $limit * @param int $offset * @param bool $order * @param bool $escape_eol * * @return string */ private static function build_db_logs_query( $filters = false, $limit = 200, $offset = 0, $order = false, $escape_eol = false ) { global $wpdb; $select = '*'; if ( $escape_eol ) { $select = ''; for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) { if ( $i > 0 ) { $select .= ', '; } if ( 'message' !== self::$_log_columns[ $i ] ) { $select .= self::$_log_columns[ $i ]; } else { $select .= 'REPLACE(message , \'\n\', \' \') AS message'; } } } $query = "SELECT {$select} FROM {$wpdb->prefix}fs_logger"; if ( is_array( $filters ) ) { $criteria = array(); if ( ! empty( $filters['type'] ) && 'all' !== $filters['type'] ) { $filters['type'] = strtolower( $filters['type'] ); switch ( $filters['type'] ) { case 'warn_error': $criteria[] = array( 'col' => 'type', 'val' => array( 'warn', 'error' ) ); break; case 'error': case 'warn': $criteria[] = array( 'col' => 'type', 'val' => $filters['type'] ); break; case 'info': default: $criteria[] = array( 'col' => 'type', 'val' => array( 'info', 'log' ) ); break; } } if ( ! empty( $filters['request_type'] ) ) { $filters['request_type'] = strtolower( $filters['request_type'] ); if ( in_array( $filters['request_type'], array( 'call', 'ajax', 'cron' ) ) ) { $criteria[] = array( 'col' => 'request_type', 'val' => $filters['request_type'] ); } } if ( ! empty( $filters['file'] ) ) { $criteria[] = array( 'col' => 'file', 'op' => 'LIKE', 'val' => '%' . esc_sql( $filters['file'] ), ); } if ( ! empty( $filters['function'] ) ) { $criteria[] = array( 'col' => 'function', 'op' => 'LIKE', 'val' => '%' . esc_sql( $filters['function'] ), ); } if ( ! empty( $filters['process_id'] ) && is_numeric( $filters['process_id'] ) ) { $criteria[] = array( 'col' => 'process_id', 'val' => $filters['process_id'] ); } if ( ! empty( $filters['logger'] ) ) { $criteria[] = array( 'col' => 'logger', 'op' => 'LIKE', 'val' => '%' . esc_sql( $filters['logger'] ) . '%', ); } if ( ! empty( $filters['message'] ) ) { $criteria[] = array( 'col' => 'message', 'op' => 'LIKE', 'val' => '%' . esc_sql( $filters['message'] ) . '%', ); } if ( 0 < count( $criteria ) ) { $query .= "\nWHERE\n"; $first = true; foreach ( $criteria as $c ) { if ( ! $first ) { $query .= "AND\n"; } if ( is_array( $c['val'] ) ) { $operator = 'IN'; for ( $i = 0, $len = count( $c['val'] ); $i < $len; $i ++ ) { $c['val'][ $i ] = "'" . esc_sql( $c['val'][ $i ] ) . "'"; } $val = '(' . implode( ',', $c['val'] ) . ')'; } else { $operator = ! empty( $c['op'] ) ? $c['op'] : '='; $val = "'" . esc_sql( $c['val'] ) . "'"; } $query .= "`{$c['col']}` {$operator} {$val}\n"; $first = false; } } } if ( ! is_array( $order ) ) { $order = array( 'col' => 'id', 'order' => 'desc' ); } $query .= " ORDER BY {$order['col']} {$order['order']} LIMIT {$offset},{$limit}"; return $query; } /** * Load logs from DB. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @param bool $filters * @param int $limit * @param int $offset * @param bool $order * * @return object[]|null */ public static function load_db_logs( $filters = false, $limit = 200, $offset = 0, $order = false ) { global $wpdb; $query = self::build_db_logs_query( $filters, $limit, $offset, $order ); return $wpdb->get_results( $query ); } /** * Load logs from DB. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @param bool $filters * @param string $filename * @param int $limit * @param int $offset * @param bool $order * * @return false|string File download URL or false on failure. */ public static function download_db_logs( $filters = false, $filename = '', $limit = 10000, $offset = 0, $order = false ) { global $wpdb; $query = self::build_db_logs_query( $filters, $limit, $offset, $order, true ); $upload_dir = wp_upload_dir(); if ( empty( $filename ) ) { $filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv'; } $filepath = rtrim( $upload_dir['path'], '/' ) . "/{$filename}"; $query .= " INTO OUTFILE '{$filepath}' FIELDS TERMINATED BY '\t' ESCAPED BY '\\\\' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\\n'"; $columns = ''; for ( $i = 0, $len = count( self::$_log_columns ); $i < $len; $i ++ ) { if ( $i > 0 ) { $columns .= ', '; } $columns .= "'" . self::$_log_columns[ $i ] . "'"; } $query = "SELECT {$columns} UNION ALL " . $query; $result = $wpdb->query( $query ); if ( false === $result ) { return false; } return rtrim( $upload_dir['url'], '/' ) . '/' . $filename; } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @param string $filename * * @return string */ public static function get_logs_download_url( $filename = '' ) { $upload_dir = wp_upload_dir(); if ( empty( $filename ) ) { $filename = 'fs-logs-' . date( 'Y-m-d_H-i-s', WP_FS__SCRIPT_START_TIME ) . '.csv'; } return rtrim( $upload_dir['url'], '/' ) . $filename; } #endregion } freemius/includes/class-fs-lock.php000064400000005451147600046700013354 0ustar00_lock_id = $lock_id; if ( ! isset( self::$_thread_id ) ) { self::$_thread_id = mt_rand( 0, 32000 ); } } /** * Try to acquire lock. If the lock is already set or is being acquired by another locker, don't do anything. * * @param int $expiration * * @return bool TRUE if successfully acquired lock. */ function try_lock( $expiration = 0 ) { if ( $this->is_locked() ) { // Already locked. return false; } set_site_transient( $this->_lock_id, self::$_thread_id, $expiration ); if ( $this->has_lock() ) { $this->lock($expiration); return true; } return false; } /** * Acquire lock regardless if it's already acquired by another locker or not. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @param int $expiration */ function lock( $expiration = 0 ) { set_site_transient( $this->_lock_id, true, $expiration ); } /** * Checks if lock is currently acquired. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @return bool */ function is_locked() { return ( false !== get_site_transient( $this->_lock_id ) ); } /** * Unlock the lock. * * @author Vova Feldman (@svovaf) * @since 2.1.0 */ function unlock() { delete_site_transient( $this->_lock_id ); } /** * Checks if lock is currently acquired by the current locker. * * @return bool */ protected function has_lock() { return ( self::$_thread_id == get_site_transient( $this->_lock_id ) ); } }freemius/includes/class-fs-garbage-collector.php000064400000037732147600046700016007 0ustar00 Map of product slugs to their last load timestamp, only for products that are not active. */ private $_gc_timestamp; /** * @var array> Map of product slugs to their data, as stored by the primary storage of `Freemius` class. */ private $_storage_data; function __construct( FS_Options $_accounts, $option_names, $type ) { $this->_accounts = $_accounts; $this->_options_names = $option_names; $this->_type = $type; $this->_plural_type = ( $type . 's' ); } function clean() { $this->_gc_timestamp = $this->_accounts->get_option( 'gc_timestamp', array() ); $this->_storage_data = $this->_accounts->get_option( $this->_type . '_data', array() ); $options = $this->load_options(); $has_updated_option = false; $filtered_products = $this->get_filtered_products(); $products_to_clean = $filtered_products['products_to_clean']; $active_products_by_id_map = $filtered_products['active_products_by_id_map']; foreach( $products_to_clean as $product ) { $slug = $product->slug; // Clear the product's data. foreach( $options as $option_name => $option ) { $updated = false; /** * We expect to deal with only array like options here. * @todo - Refactor this to create dedicated GC classes for every option, then we can make the code mode predictable. * For example, depending on data integrity of `plugins` we can still miss something entirely in the `plugin_data` or vice-versa. * A better algorithm is to iterate over all options individually in separate classes and check against primary storage to see if those can be garbage collected. * But given the chance of data integrity issue is very low, we let this run for now and gather feedback. */ if ( ! is_array( $option ) ) { continue; } if ( array_key_exists( $slug, $option ) ) { unset( $option[ $slug ] ); $updated = true; } else if ( array_key_exists( "{$slug}:{$this->_type}", $option ) ) { /* admin_notices */ unset( $option[ "{$slug}:{$this->_type}" ] ); $updated = true; } else if ( isset( $product->id ) && array_key_exists( $product->id, $option ) ) { /* all_licenses, add-ons, and id_slug_type_path_map */ $is_inactive_by_id = ! isset( $active_products_by_id_map[ $product->id ] ); $is_inactive_by_slug = ( 'id_slug_type_path_map' === $option_name && ( ! isset( $option[ $product->id ]['slug'] ) || $slug === $option[ $product->id ]['slug'] ) ); if ( $is_inactive_by_id || $is_inactive_by_slug ) { unset( $option[ $product->id ] ); $updated = true; } } else if ( /* file_slug_map */ isset( $product->file ) && array_key_exists( $product->file, $option ) && $slug === $option[ $product->file ] ) { unset( $option[ $product->file ] ); $updated = true; } if ( $updated ) { $this->_accounts->set_option( $option_name, $option ); $options[ $option_name ] = $option; $has_updated_option = true; } } // Clear the product's data from the primary storage. if ( isset( $this->_storage_data[ $slug ] ) ) { unset( $this->_storage_data[ $slug ] ); $has_updated_option = true; } // Clear from GC timestamp. // @todo - This perhaps needs a separate garbage collector for all expired products. But the chance of left-over is very slim. if ( isset( $this->_gc_timestamp[ $slug ] ) ) { unset( $this->_gc_timestamp[ $slug ] ); $has_updated_option = true; } } $this->_accounts->set_option( 'gc_timestamp', $this->_gc_timestamp ); $this->_accounts->set_option( $this->_type . '_data', $this->_storage_data ); return $has_updated_option; } private function get_all_option_names() { return array_merge( array( 'admin_notices', 'updates', 'all_licenses', 'addons', 'id_slug_type_path_map', 'file_slug_map', ), $this->_options_names ); } private function get_products() { $products = $this->_accounts->get_option( $this->_plural_type, array() ); // Fill any missing product found in the primary storage. // @todo - This wouldn't be needed if we use dedicated GC design for every options. The options themselves would provide such information. foreach( $this->_storage_data as $slug => $product_data ) { if ( ! isset( $products[ $slug ] ) ) { $products[ $slug ] = (object) $product_data; } // This is needed to handle a scenario in which there are duplicate sets of data for the same product, but one of them needs to be removed. $products[ $slug ] = clone $products[ $slug ]; // The reason for having the line above. This also handles a scenario in which the slug is either empty or not empty but incorrect. $products[ $slug ]->slug = $slug; } $this->update_gc_timestamp( $products ); return $products; } private function get_filtered_products() { $products_to_clean = array(); $active_products_by_id_map = array(); $products = $this->get_products(); foreach ( $products as $slug => $product_data ) { if ( ! is_object( $product_data ) ) { continue; } if ( $this->is_product_active( $slug ) ) { $active_products_by_id_map[ $product_data->id ] = true; continue; } $is_addon = ( ! empty( $product_data->parent_plugin_id ) ); if ( ! $is_addon ) { $products_to_clean[] = $product_data; } else { /** * If add-on, add to the beginning of the array so that add-ons are removed before their parent. This is to prevent an unexpected issue when an add-on exists but its parent was already removed. */ array_unshift( $products_to_clean, $product_data ); } } return array( 'products_to_clean' => $products_to_clean, 'active_products_by_id_map' => $active_products_by_id_map, ); } /** * @param string $slug * * @return bool */ private function is_product_active( $slug ) { $instances = Freemius::_get_all_instances(); foreach ( $instances as $instance ) { if ( $instance->get_slug() === $slug ) { return true; } } $expiration_time = fs_get_optional_constant( 'WP_FS__GARBAGE_COLLECTOR_EXPIRATION_TIME_SECS', ( WP_FS__TIME_WEEK_IN_SEC * 4 ) ); if ( $this->get_last_load_timestamp( $slug ) > ( time() - $expiration_time ) ) { // Last activation was within the last 4 weeks. return true; } return false; } private function load_options() { $options = array(); $option_names = $this->get_all_option_names(); foreach ( $option_names as $option_name ) { $options[ $option_name ] = $this->_accounts->get_option( $option_name, array() ); } return $options; } /** * Updates the garbage collector timestamp, only if it was not already set by the product's primary storage. * * @param array $products * * @return void */ private function update_gc_timestamp( $products ) { foreach ($products as $slug => $product_data) { if ( ! is_object( $product_data ) && ! is_array( $product_data ) ) { continue; } // If the product is active, we don't need to update the gc_timestamp. if ( isset( $this->_storage_data[ $slug ]['last_load_timestamp'] ) ) { continue; } // First try to check if the product is present in the primary storage. If so update that. if ( isset( $this->_storage_data[ $slug ] ) ) { $this->_storage_data[ $slug ]['last_load_timestamp'] = time(); } else if ( ! isset( $this->_gc_timestamp[ $slug ] ) ) { // If not, fallback to the gc_timestamp, but we don't want to update it more than once. $this->_gc_timestamp[ $slug ] = time(); } } } private function get_last_load_timestamp( $slug ) { if ( isset( $this->_storage_data[ $slug ]['last_load_timestamp'] ) ) { return $this->_storage_data[ $slug ]['last_load_timestamp']; } return isset( $this->_gc_timestamp[ $slug ] ) ? $this->_gc_timestamp[ $slug ] : // This should never happen, but if it does, let's assume the product is not expired. time(); } } class FS_User_Garbage_Collector implements FS_I_Garbage_Collector { private $_accounts; private $_types; function __construct( FS_Options $_accounts, array $types ) { $this->_accounts = $_accounts; $this->_types = $types; } function clean() { $users = Freemius::get_all_users(); $user_has_install_map = $this->get_user_has_install_map(); if ( count( $users ) === count( $user_has_install_map ) ) { return false; } $products_user_id_license_ids_map = $this->_accounts->get_option( 'user_id_license_ids_map', array() ); $has_updated_option = false; foreach ( $users as $user_id => $user ) { if ( ! isset( $user_has_install_map[ $user_id ] ) ) { unset( $users[ $user_id ] ); foreach( $products_user_id_license_ids_map as $product_id => $user_id_license_ids_map ) { unset( $user_id_license_ids_map[ $user_id ] ); if ( empty( $user_id_license_ids_map ) ) { unset( $products_user_id_license_ids_map[ $product_id ] ); } else { $products_user_id_license_ids_map[ $product_id ] = $user_id_license_ids_map; } } $this->_accounts->set_option( 'users', $users ); $this->_accounts->set_option( 'user_id_license_ids_map', $products_user_id_license_ids_map ); $has_updated_option = true; } } return $has_updated_option; } private function get_user_has_install_map() { $user_has_install_map = array(); foreach ( $this->_types as $product_type ) { $option_name = ( WP_FS__MODULE_TYPE_PLUGIN !== $product_type ) ? "{$product_type}_sites" : 'sites'; $installs = $this->_accounts->get_option( $option_name, array() ); foreach ( $installs as $install ) { $user_has_install_map[ $install->user_id ] = true; } } return $user_has_install_map; } } // Main entry-level class. class FS_Garbage_Collector implements FS_I_Garbage_Collector { /** * @var FS_Garbage_Collector * @since 2.6.0 */ private static $_instance; /** * @return FS_Garbage_Collector */ static function instance() { if ( ! isset( self::$_instance ) ) { self::$_instance = new self(); } return self::$_instance; } #endregion private function __construct() { } function clean() { $_accounts = FS_Options::instance( WP_FS__ACCOUNTS_OPTION_NAME, true ); $products_cleaners = $this->get_product_cleaners( $_accounts ); $has_cleaned = false; foreach ( $products_cleaners as $products_cleaner ) { if ( $products_cleaner->clean() ) { $has_cleaned = true; } } if ( $has_cleaned ) { $user_cleaner = new FS_User_Garbage_Collector( $_accounts, array_keys( $products_cleaners ) ); $user_cleaner->clean(); } // @todo - We need a garbage collector for `all_plugins` and `active_plugins` (and variants of themes). // Always store regardless of whether there were cleaned products or not since during the process, the logic may set the last load timestamp of some products. $_accounts->store(); } /** * @param FS_Options $_accounts * * @return FS_I_Garbage_Collector[] */ private function get_product_cleaners( FS_Options $_accounts ) { /** * @var FS_I_Garbage_Collector[] $products_cleaners */ $products_cleaners = array(); $products_cleaners[ WP_FS__MODULE_TYPE_PLUGIN ] = new FS_Product_Garbage_Collector( $_accounts, array( 'sites', 'plans', 'plugins', ), WP_FS__MODULE_TYPE_PLUGIN ); $products_cleaners[ WP_FS__MODULE_TYPE_THEME ] = new FS_Product_Garbage_Collector( $_accounts, array( 'theme_sites', 'theme_plans', 'themes', ), WP_FS__MODULE_TYPE_THEME ); return $products_cleaners; } }freemius/includes/class-fs-api.php000064400000052561147600046700013201 0ustar00get_option( 'api_clock_diff', 0 ); Freemius_Api_WordPress::SetClockDiff( self::$_clock_diff ); if ( self::$_options->get_option( 'api_force_http', false ) ) { Freemius_Api_WordPress::SetHttp(); } } /** * @param string $slug * @param string $scope 'app', 'developer', 'user' or 'install'. * @param number $id Element's id. * @param string $public_key Public key. * @param bool|string $secret_key Element's secret key. * @param bool $is_sandbox * @param null|string $sdk_version * @param null|string $url */ private function __construct( $slug, $scope, $id, $public_key, $secret_key, $is_sandbox, $sdk_version, $url ) { $this->_api = new Freemius_Api_WordPress( $scope, $id, $public_key, $secret_key, $is_sandbox ); $this->_slug = $slug; $this->_sdk_version = $sdk_version; $this->_url = $url; $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $slug . '_api', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); } /** * Find clock diff between server and API server, and store the diff locally. * * @param bool|int $diff * * @return bool|int False if clock diff didn't change, otherwise returns the clock diff in seconds. */ private function _sync_clock_diff( $diff = false ) { $this->_logger->entrance(); // Sync clock and store. $new_clock_diff = ( false === $diff ) ? Freemius_Api_WordPress::FindClockDiff() : $diff; if ( $new_clock_diff === self::$_clock_diff ) { return false; } self::$_clock_diff = $new_clock_diff; // Update API clock's diff. Freemius_Api_WordPress::SetClockDiff( self::$_clock_diff ); // Store new clock diff in storage. self::$_options->set_option( 'api_clock_diff', self::$_clock_diff, true ); return $new_clock_diff; } /** * Override API call to enable retry with servers' clock auto sync method. * * @param string $path * @param string $method * @param array $params * @param bool $in_retry Is in retry or first call attempt. * * @return array|mixed|string|void */ private function _call( $path, $method = 'GET', $params = array(), $in_retry = false ) { $this->_logger->entrance( $method . ':' . $path ); $force_http = ( ! $in_retry && self::$_options->get_option( 'api_force_http', false ) ); if ( self::is_temporary_down() ) { $result = $this->get_temporary_unavailable_error(); } else { /** * @since 2.3.0 Include the SDK version with all API requests that going through the API manager. IMPORTANT: Only pass the SDK version if the caller didn't include it yet. */ if ( ! empty( $this->_sdk_version ) ) { if ( false === strpos( $path, 'sdk_version=' ) && ! isset( $params['sdk_version'] ) ) { // Always add the sdk_version param in the querystring. DO NOT INCLUDE IT IN THE BODY PARAMS, OTHERWISE, IT MAY LEAD TO AN UNEXPECTED PARAMS PARSING IN CASES WHERE THE $params IS A REGULAR NON-ASSOCIATIVE ARRAY. $path = add_query_arg( 'sdk_version', $this->_sdk_version, $path ); } } /** * @since 2.5.0 Include the site's URL, if available, in all API requests that are going through the API manager. */ if ( ! empty( $this->_url ) ) { if ( false === strpos( $path, 'url=' ) && ! isset( $params['url'] ) ) { $path = add_query_arg( 'url', $this->_url, $path ); } } $result = $this->_api->Api( $path, $method, $params ); if ( ! $in_retry && null !== $result && isset( $result->error ) && isset( $result->error->code ) ) { $retry = false; if ( 'request_expired' === $result->error->code ) { $diff = isset( $result->error->timestamp ) ? ( time() - strtotime( $result->error->timestamp ) ) : false; // Try to sync clock diff. if ( false !== $this->_sync_clock_diff( $diff ) ) { // Retry call with new synced clock. $retry = true; } } else if ( Freemius_Api_WordPress::IsHttps() && FS_Api::is_ssl_error_response( $result ) ) { $force_http = true; $retry = true; } if ( $retry ) { if ( $force_http ) { $this->toggle_force_http( true ); } $result = $this->_call( $path, $method, $params, true ); } } } if ( self::is_api_error( $result ) ) { if ( $this->_logger->is_on() ) { // Log API errors. $this->_logger->api_error( $result ); } if ( $force_http ) { $this->toggle_force_http( false ); } } return $result; } /** * Override API call to wrap it in servers' clock sync method. * * @param string $path * @param string $method * @param array $params * * @return array|mixed|string|void * @throws Freemius_Exception */ function call( $path, $method = 'GET', $params = array() ) { return $this->_call( $path, $method, $params ); } /** * Get API request URL signed via query string. * * @param string $path * * @return string */ function get_signed_url( $path ) { return $this->_api->GetSignedUrl( $path ); } /** * @param string $path * @param bool $flush * @param int $expiration (optional) Time until expiration in seconds from now, defaults to 24 hours * * @return stdClass|mixed */ function get( $path = '/', $flush = false, $expiration = WP_FS__TIME_24_HOURS_IN_SEC ) { $this->_logger->entrance( $path ); $cache_key = $this->get_cache_key( $path ); // Always flush during development. if ( WP_FS__DEV_MODE || $this->_api->IsSandbox() ) { $flush = true; } $has_valid_cache = self::$_cache->has_valid( $cache_key, $expiration ); $cached_result = $has_valid_cache ? self::$_cache->get( $cache_key ) : null; if ( $flush || is_null( $cached_result ) ) { $result = $this->call( $path ); if ( ! is_object( $result ) || isset( $result->error ) ) { // Api returned an error. if ( is_object( $cached_result ) && ! isset( $cached_result->error ) ) { // If there was an error during a newer data fetch, // fallback to older data version. $result = $cached_result; if ( $this->_logger->is_on() ) { $this->_logger->warn( 'Fallback to cached API result: ' . var_export( $cached_result, true ) ); } } else { if ( is_object( $result ) && isset( $result->error->http ) && 404 == $result->error->http ) { /** * If the response code is 404, cache the result for half of the `$expiration`. * * @author Leo Fajardo (@leorw) * @since 2.2.4 */ $expiration /= 2; } else { // If no older data version and the response code is not 404, return result without // caching the error. return $result; } } } if ( is_numeric( $expiration ) ) { self::$_cache->set( $cache_key, $result, $expiration ); } $cached_result = $result; } else { $this->_logger->log( 'Using cached API result.' ); } return $cached_result; } /** * @todo Remove this method after migrating Freemius::safe_remote_post() to FS_Api::call(). * * @author Leo Fajardo (@leorw) * @since 2.5.4 * * @param string $url * @param array $remote_args * * @return array|WP_Error The response array or a WP_Error on failure. */ static function remote_request( $url, $remote_args ) { if ( ! class_exists( 'Freemius_Api_WordPress' ) ) { require_once WP_FS__DIR_SDK . '/FreemiusWordPress.php'; } if ( method_exists( 'Freemius_Api_WordPress', 'RemoteRequest' ) ) { return Freemius_Api_WordPress::RemoteRequest( $url, $remote_args ); } // The following is for backward compatibility when a modified PHP SDK version is in use and the `Freemius_Api_WordPress:RemoteRequest()` method doesn't exist. $response = wp_remote_request( $url, $remote_args ); if ( is_array( $response ) && ( empty( $response['headers'] ) || empty( $response['headers']['x-api-server'] ) ) ) { // API is considered blocked if the response doesn't include the `x-api-server` header. When there's no error but this header doesn't exist, the response is usually not in the expected form (e.g., cannot be JSON-decoded). $response = new WP_Error( 'api_blocked', htmlentities( $response['body'] ) ); } return $response; } /** * Check if there's a cached version of the API request. * * @author Vova Feldman (@svovaf) * @since 1.2.1 * * @param string $path * @param string $method * @param array $params * * @return bool */ function is_cached( $path, $method = 'GET', $params = array() ) { $cache_key = $this->get_cache_key( $path, $method, $params ); return self::$_cache->has_valid( $cache_key ); } /** * Invalidate a cached version of the API request. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param string $path * @param string $method * @param array $params */ function purge_cache( $path, $method = 'GET', $params = array() ) { $this->_logger->entrance( "{$method}:{$path}" ); $cache_key = $this->get_cache_key( $path, $method, $params ); self::$_cache->purge( $cache_key ); } /** * Invalidate a cached version of the API request. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $path * @param int $expiration * @param string $method * @param array $params */ function update_cache_expiration( $path, $expiration = WP_FS__TIME_24_HOURS_IN_SEC, $method = 'GET', $params = array() ) { $this->_logger->entrance( "{$method}:{$path}:{$expiration}" ); $cache_key = $this->get_cache_key( $path, $method, $params ); self::$_cache->update_expiration( $cache_key, $expiration ); } /** * @param string $path * @param string $method * @param array $params * * @return string * @throws \Freemius_Exception */ private function get_cache_key( $path, $method = 'GET', $params = array() ) { $canonized = $this->_api->CanonizePath( $path ); // $exploded = explode('/', $canonized); // return $method . '_' . array_pop($exploded) . '_' . md5($canonized . json_encode($params)); return strtolower( $method . ':' . $canonized ) . ( ! empty( $params ) ? '#' . md5( json_encode( $params ) ) : '' ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 * * @param bool $is_http */ private function toggle_force_http( $is_http ) { self::$_options->set_option( 'api_force_http', $is_http, true ); if ( $is_http ) { Freemius_Api_WordPress::SetHttp(); } else if ( method_exists( 'Freemius_Api_WordPress', 'SetHttps' ) ) { Freemius_Api_WordPress::SetHttps(); } } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 * * @param mixed $response * * @return bool */ static function is_blocked( $response ) { return ( self::is_api_error_object( $response, true ) && isset( $response->error->code ) && 'api_blocked' === $response->error->code ); } /** * Check if API is temporary down. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @return bool */ static function is_temporary_down() { self::_init(); $test = self::$_cache->get_valid( 'ping_test', null ); return ( false === $test ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @return object */ private function get_temporary_unavailable_error() { return (object) array( 'error' => (object) array( 'type' => 'TemporaryUnavailable', 'message' => 'API is temporary unavailable, please retry in ' . ( self::$_cache->get_record_expiration( 'ping_test' ) - WP_FS__SCRIPT_START_TIME ) . ' sec.', 'code' => 'temporary_unavailable', 'http' => 503 ) ); } /** * Check if based on the API result we should try * to re-run the same request with HTTP instead of HTTPS. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param $result * * @return bool */ private static function should_try_with_http( $result ) { if ( ! Freemius_Api_WordPress::IsHttps() ) { return false; } return ( ! is_object( $result ) || ! isset( $result->error ) || ! isset( $result->error->code ) || ! in_array( $result->error->code, array( 'curl_missing', 'cloudflare_ddos_protection', 'maintenance_mode', 'squid_cache_block', 'too_many_requests', ) ) ); } function get_url( $path = '' ) { return Freemius_Api_WordPress::GetUrl( $path, $this->_api->IsSandbox() ); } /** * Clear API cache. * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ static function clear_cache() { self::_init(); self::$_cache = FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME ); self::$_cache->clear(); } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 */ static function clear_force_http_flag() { self::$_options->unset_option( 'api_force_http' ); } #---------------------------------------------------------------------------------- #region Error Handling #---------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $result * * @return bool Is API result contains an error. */ static function is_api_error( $result ) { return ( is_object( $result ) && isset( $result->error ) ) || is_string( $result ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param mixed $result * @param bool $ignore_message * * @return bool Is API result contains an error. */ static function is_api_error_object( $result, $ignore_message = false ) { return ( is_object( $result ) && isset( $result->error ) && ( $ignore_message || isset( $result->error->message ) ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 * * @param WP_Error|object|string $response * * @return bool */ static function is_ssl_error_response( $response ) { $http_error = null; if ( $response instanceof WP_Error ) { if ( isset( $response->errors ) && isset( $response->errors['http_request_failed'] ) ) { $http_error = strtolower( $response->errors['http_request_failed'][0] ); } } else if ( self::is_api_error_object( $response ) && ! empty( $response->error->message ) ) { $http_error = $response->error->message; } return ( ! empty( $http_error ) && ( false !== strpos( $http_error, 'curl error 35' ) || ( false === strpos( $http_error, '' ) && false !== strpos( $http_error, 'ssl' ) ) ) ); } /** * Checks if given API result is a non-empty and not an error object. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $result * @param string|null $required_property Optional property we want to verify that is set. * * @return bool */ static function is_api_result_object( $result, $required_property = null ) { return ( is_object( $result ) && ! isset( $result->error ) && ( empty( $required_property ) || isset( $result->{$required_property} ) ) ); } /** * Checks if given API result is a non-empty entity object with non-empty ID. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $result * * @return bool */ static function is_api_result_entity( $result ) { return self::is_api_result_object( $result, 'id' ) && FS_Entity::is_valid_id( $result->id ); } /** * Get API result error code. If failed to get code, returns an empty string. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param mixed $result * * @return string */ static function get_error_code( $result ) { if ( is_object( $result ) && isset( $result->error ) && is_object( $result->error ) && ! empty( $result->error->code ) ) { return $result->error->code; } return ''; } #endregion }freemius/includes/class-fs-admin-notices.php000064400000027167147600046700015166 0ustar00_id = $id; $this->_title = $title; $this->_module_unique_affix = $module_unique_affix; $this->_is_multisite = is_multisite(); if ( $this->_is_multisite ) { $this->_blog_id = get_current_blog_id(); $this->_network_notices = FS_Admin_Notice_Manager::instance( $id, $title, $module_unique_affix, $is_network_and_blog_admins, true ); } $this->_notices = FS_Admin_Notice_Manager::instance( $id, $title, $module_unique_affix, false, $this->_blog_id ); } /** * Add admin message to admin messages queue, and hook to admin_notices / all_admin_notices if not yet hooked. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param string $message * @param string $title * @param string $type * @param bool $is_sticky * @param string $id Message ID * @param bool $store_if_sticky * @param int|null $network_level_or_blog_id * * @uses add_action() */ function add( $message, $title = '', $type = 'success', $is_sticky = false, $id = '', $store_if_sticky = true, $network_level_or_blog_id = null, $is_dimissible = null ) { $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id ); $notices->add( $message, $title, $type, $is_sticky, $id, $store_if_sticky, null, null, false, $is_dimissible ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param string|string[] $ids * @param int|null $network_level_or_blog_id * @param bool $store */ function remove_sticky( $ids, $network_level_or_blog_id = null, $store = true ) { if ( ! is_array( $ids ) ) { $ids = array( $ids ); } if ( $this->should_use_network_notices( $ids[0], $network_level_or_blog_id ) ) { $notices = $this->_network_notices; } else { $notices = $this->get_site_notices( $network_level_or_blog_id ); } return $notices->remove_sticky( $ids, $store ); } /** * Check if sticky message exists by id. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string $id * @param int|null $network_level_or_blog_id * * @return bool */ function has_sticky( $id, $network_level_or_blog_id = null ) { $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id ); return $notices->has_sticky( $id ); } /** * Adds sticky admin notification. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param string $message * @param string $id Message ID * @param string $title * @param string $type * @param int|null $network_level_or_blog_id * @param number|null $wp_user_id * @param string|null $plugin_title * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and * blog admin pages. * @param bool $is_dismissible */ function add_sticky( $message, $id, $title = '', $type = 'success', $network_level_or_blog_id = null, $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false, $is_dismissible = true, $data = array() ) { $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id ); $notices->add_sticky( $message, $id, $title, $type, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dismissible, $data ); } /** * Retrieves the data of a sticky notice. * * @author Leo Fajardo (@leorw) * @since 2.4.3 * * @param string $id * @param int|null $network_level_or_blog_id * * @return array|null */ function get_sticky( $id, $network_level_or_blog_id ) { $notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id ); return $notices->get_sticky( $id ); } /** * Clear all sticky messages. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int|null $network_level_or_blog_id * @param bool $is_temporary */ function clear_all_sticky( $network_level_or_blog_id = null, $is_temporary = false ) { if ( ! $this->_is_multisite || false === $network_level_or_blog_id || 0 == $network_level_or_blog_id || is_null( $network_level_or_blog_id ) ) { $notices = $this->get_site_notices( $network_level_or_blog_id ); $notices->clear_all_sticky( $is_temporary ); } if ( $this->_is_multisite && ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) ) ) { $this->_network_notices->clear_all_sticky( $is_temporary ); } } /** * Add admin message to all admin messages queue, and hook to all_admin_notices if not yet hooked. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param string $message * @param string $title * @param string $type * @param bool $is_sticky * @param string $id Message ID */ function add_all( $message, $title = '', $type = 'success', $is_sticky = false, $id = '' ) { $this->add( $message, $title, $type, $is_sticky, true, $id ); } #-------------------------------------------------------------------------------- #region Helper Methods #-------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id * * @return FS_Admin_Notice_Manager */ private function get_site_notices( $blog_id = 0 ) { if ( 0 == $blog_id || $blog_id == $this->_blog_id ) { return $this->_notices; } return FS_Admin_Notice_Manager::instance( $this->_id, $this->_title, $this->_module_unique_affix, false, $blog_id ); } /** * Check if the network notices should be used. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $id * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite notices (if there's a network). When `false`, use the current context blog notices. When `null`, the decision which notices manager to use (MS vs. Current S) will be handled internally and determined based on the $id and the context admin (blog admin vs. network level admin). * * @return bool */ private function should_use_network_notices( $id = '', $network_level_or_blog_id = null ) { if ( ! $this->_is_multisite ) { // Not a multisite environment. return false; } if ( is_numeric( $network_level_or_blog_id ) ) { // Explicitly asked to use a specified blog storage. return false; } if ( is_bool( $network_level_or_blog_id ) ) { // Explicitly specified whether should use the network or blog level storage. return $network_level_or_blog_id; } return fs_is_network_admin(); } /** * Retrieves an instance of FS_Admin_Notice_Manager. * * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param string $id * @param int|null $network_level_or_blog_id * * @return FS_Admin_Notice_Manager */ private function get_site_or_network_notices( $id, $network_level_or_blog_id ) { return $this->should_use_network_notices( $id, $network_level_or_blog_id ) ? $this->_network_notices : $this->get_site_notices( $network_level_or_blog_id ); } #endregion }freemius/includes/class-freemius.php000064400003606111147600046700013640 0ustar00store_id_slug_type_path_map( $module_id, $slug ); } $this->_module_id = $module_id; $this->_slug = $this->get_slug(); $this->_module_type = $this->get_module_type(); $this->_blog_id = is_multisite() ? get_current_blog_id() : null; $this->_storage = FS_Storage::instance( $this->_module_type, $this->_slug ); // If not set or 24 hours have already passed from the last time it's set, set the last load timestamp to the current time. if ( ! isset( $this->_storage->last_load_timestamp ) || $this->_storage->last_load_timestamp < ( time() - ( WP_FS__TIME_24_HOURS_IN_SEC ) ) ) { $this->_storage->last_load_timestamp = time(); } $this->_cache = FS_Cache_Manager::get_manager( WP_FS___OPTION_PREFIX . "cache_{$module_id}" ); $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $this->get_unique_affix(), WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); $this->_plugin_main_file_path = $this->_find_caller_plugin_file( $is_init, $main_file ); $this->_plugin_dir_path = plugin_dir_path( $this->_plugin_main_file_path ); $this->_plugin_basename = $this->get_plugin_basename(); $this->_free_plugin_basename = str_replace( '-premium/', '/', $this->_plugin_basename ); $this->_is_multisite_integrated = ( defined( "WP_FS__PRODUCT_{$module_id}_MULTISITE" ) && ( true === constant( "WP_FS__PRODUCT_{$module_id}_MULTISITE" ) ) ); $this->_is_network_active = ( is_multisite() && $this->_is_multisite_integrated && // Themes are always network activated, but the ACTUAL activation is per site. $this->is_plugin() && ( is_plugin_active_for_network( $this->_plugin_basename ) || // Plugin network level activation or uninstall. ( fs_is_network_admin() && is_plugin_inactive( $this->_plugin_basename ) ) ) ); $this->_storage->set_network_active( $this->_is_network_active, $this->is_delegated_connection() ); if ( ! isset( $this->_storage->is_network_activated ) ) { $this->_storage->is_network_activated = $this->_is_network_active; } if ( $this->_storage->is_network_activated != $this->_is_network_active ) { // Update last activation level. $this->_storage->is_network_activated = $this->_is_network_active; $this->maybe_adjust_storage(); } #region Migration if ( is_multisite() ) { /** * If the install_timestamp exists on the site level but doesn't exist on the * network level storage, it means that we need to process the storage with migration. * * The code in this `if` scope will only be executed once and only for the first site that will execute it because once we migrate the storage data, install_timestamp will be already set in the network level storage. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ if ( false === $this->_storage->get( 'install_timestamp', false, true ) && false !== $this->_storage->get( 'install_timestamp', false, false ) ) { // Initiate storage migration. $this->_storage->migrate_to_network(); // Migrate module cache to network level storage. $this->_cache->migrate_to_network(); } } #endregion $base_name_split = explode( '/', $this->_plugin_basename ); $this->_plugin_dir_name = $base_name_split[0]; if ( $this->_logger->is_on() ) { $this->_logger->info( 'plugin_main_file_path = ' . $this->_plugin_main_file_path ); $this->_logger->info( 'plugin_dir_path = ' . $this->_plugin_dir_path ); $this->_logger->info( 'plugin_basename = ' . $this->_plugin_basename ); $this->_logger->info( 'free_plugin_basename = ' . $this->_free_plugin_basename ); $this->_logger->info( 'plugin_dir_name = ' . $this->_plugin_dir_name ); } // Remember link between file to slug. $this->store_file_slug_map(); // Store plugin's initial install timestamp. if ( ! isset( $this->_storage->install_timestamp ) ) { $this->_storage->install_timestamp = WP_FS__SCRIPT_START_TIME; } if ( ! is_object( $this->_plugin ) ) { $this->_plugin = FS_Plugin_Manager::instance( $this->_module_id )->get(); } $this->_admin_notices = FS_Admin_Notices::instance( $this->_slug . ( $this->is_theme() ? ':theme' : '' ), /** * Ensure that the admin notice will always have a title by using the stored plugin title if available and * retrieving the title via the "get_plugin_name" method if there is no stored plugin title available. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ ( is_object( $this->_plugin ) && isset( $this->_plugin->title ) ? $this->_plugin->title : $this->get_plugin_name() ), $this->get_unique_affix() ); if ( 'true' === fs_request_get( 'fs_clear_api_cache' ) || fs_request_is_action( 'restart_freemius' ) ) { FS_Api::clear_cache(); $this->_cache->clear(); } $this->register_constructor_hooks(); /** * Starting from version 2.0.0, `FS_Site` entities no longer have the `plan` property and have `plan_id` * instead. This should be called before calling `_load_account()`, otherwise, `$this->_site` will not be * loaded in `_load_account` for versions of SDK starting from 2.0.0. * * @author Leo Fajardo (@leorw) */ self::migrate_install_plan_to_plan_id( $this->_storage ); $this->_load_account(); $this->_version_updates_handler(); } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 */ private function maybe_adjust_storage() { $install_timestamp = null; $prev_is_premium = null; $options_to_update = array(); $is_network_admin = fs_is_network_admin(); $network_install_timestamp = $this->_storage->get( 'install_timestamp', null, true ); if ( ! $is_network_admin ) { if ( is_null( $network_install_timestamp ) ) { // Plugin was not network-activated before. return; } if ( is_null( $this->_storage->get( 'install_timestamp', null, false ) ) ) { // Set the `install_timestamp` only if it's not yet set. $install_timestamp = $network_install_timestamp; } $prev_is_premium = $this->_storage->get( 'prev_is_premium', null, true ); } else { $current_wp_user = self::_get_current_wp_user(); $current_fs_user = self::_get_user_by_email( $current_wp_user->user_email ); $network_user_info = array(); $skips_count = 0; $sites = self::get_sites(); $sites_count = count( $sites ); $blog_id_2_install_map = array(); $is_first_non_ignored_blog = true; foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $blog_install_timestamp = $this->_storage->get( 'install_timestamp', null, $blog_id ); if ( is_null( $blog_install_timestamp ) ) { // Plugin has not been installed on this blog. continue; } $is_earlier_install = ( ! is_null( $install_timestamp ) && $blog_install_timestamp < $install_timestamp ); $install = $this->get_install_by_blog_id( $blog_id ); $update_network_user_info = false; if ( ! is_object( $install ) ) { if ( ! $this->_storage->get( 'is_anonymous', false, $blog_id ) ) { // The opt-in decision (whether to skip or opt in) is yet to be made. continue; } $skips_count ++; } else { $blog_id_2_install_map[ $blog_id ] = $install; if ( empty( $network_user_info ) ) { // Set the network user info for the 1st time. Choose any user information whether or not it is for the current WP user. $update_network_user_info = true; } if ( ! $update_network_user_info && is_object( $current_fs_user ) && $network_user_info['user_id'] != $current_fs_user->id && $install->user_id == $current_fs_user->id ) { // If an install that is owned by the current WP user is found, use its user information instead. $update_network_user_info = true; } if ( ! $update_network_user_info && $is_earlier_install && ( ! is_object( $current_fs_user ) || $current_fs_user->id == $install->user_id ) ) { // Update to the earliest install info if there's no install found so far that is owned by the current WP user; OR only if the found install is owned by the current WP user. $update_network_user_info = true; } } if ( $update_network_user_info ) { $network_user_info = array( 'user_id' => $install->user_id, 'blog_id' => $blog_id ); } $site_prev_is_premium = $this->_storage->get( 'prev_is_premium', null, $blog_id ); if ( $is_first_non_ignored_blog ) { $prev_is_premium = $site_prev_is_premium; if ( is_null( $network_install_timestamp ) ) { $install_timestamp = $blog_install_timestamp; } $is_first_non_ignored_blog = false; continue; } if ( ! is_null( $prev_is_premium ) && $prev_is_premium !== $site_prev_is_premium ) { // If a different `$site_prev_is_premium` value is found, do not include the option in the collection of options to update. $prev_is_premium = null; } if ( $is_earlier_install ) { // If an earlier install timestamp is found. $install_timestamp = $blog_install_timestamp; } } $installs_count = count( $blog_id_2_install_map ); if ( $sites_count === ( $installs_count + $skips_count ) ) { if ( ! empty( $network_user_info ) ) { $options_to_update['network_user_id'] = $network_user_info['user_id']; $options_to_update['network_install_blog_id'] = $network_user_info['blog_id']; foreach ( $blog_id_2_install_map as $blog_id => $install ) { if ( $install->user_id == $network_user_info['user_id'] ) { continue; } $this->_storage->store( 'is_delegated_connection', true, $blog_id ); } } if ( $sites_count === $skips_count ) { /** * Assume network-level skipping as the intended action if all actions identified were only * skipping of the connection (i.e., no opt-ins and delegated connections so far). */ $options_to_update['is_anonymous_ms'] = true; } else if ( $sites_count === $installs_count ) { /** * Assume network-level opt-in as the intended action if all actions identified were only opt-ins * (i.e., no delegation and skipping of the connections so far). */ $options_to_update['is_network_connected'] = true; } } } if ( ! is_null( $install_timestamp ) ) { $options_to_update['install_timestamp'] = $install_timestamp; } if ( ! is_null( $prev_is_premium ) ) { $options_to_update['prev_is_premium'] = $prev_is_premium; } if ( ! empty( $options_to_update ) ) { $this->adjust_storage( $options_to_update, $is_network_admin ); } } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param array $options * @param bool $is_network_admin */ private function adjust_storage( $options, $is_network_admin ) { foreach ( $options as $name => $value ) { $this->_storage->store( $name, $value, $is_network_admin ? true : null ); } } /** * Checks whether this module has a settings menu. * * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return bool */ function has_settings_menu() { return ( $this->_is_network_active && fs_is_network_admin() ) ? $this->_menu->has_network_menu() : $this->_menu->has_menu(); } /** * If `true` the opt-in should be shown as a modal dialog box on the themes.php page. WordPress.org themes guidelines prohibit from redirecting the user from the themes.php page after activating a theme. * * @author Vova Feldman (@svovaf) * @since 2.4.5 * * @return bool */ function show_opt_in_on_themes_page() { if ( ! $this->is_free_wp_org_theme() ) { return false; } if ( ! $this->has_settings_menu() ) { return true; } return $this->show_settings_with_tabs(); } /** * If `true` the opt-in should be shown on the product's main setting page. * * @author Vova Feldman (@svovaf) * @since 2.4.5 * * @return bool * * @uses show_opt_in_on_themes_page(); */ function show_opt_in_on_setting_page() { return ! $this->show_opt_in_on_themes_page(); } /** * If `true` the settings should be shown using tabs. * * @author Vova Feldman (@svovaf) * @since 2.4.5 * * @return bool */ function show_settings_with_tabs() { return ( self::NAVIGATION_TABS === $this->_navigation ); } /** * Check if the context module is free wp.org theme. * * This method is helpful because: * 1. wp.org themes are limited to a single submenu item, * and sub-submenu items are most likely not allowed (never verified). * 2. wp.org themes are not allowed to redirect the user * after the theme activation, therefore, the agreed UX * is showing the opt-in as a modal dialog box after * activation (approved by @otto42, @emiluzelac, @greenshady, @grapplerulrich). * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool */ function is_free_wp_org_theme() { return ( $this->is_theme() && $this->is_org_repo_compliant() && ! $this->is_premium() ); } /** * Checks whether this a submenu item is visible. * * @author Vova Feldman (@svovaf) * @since 1.2.2.6 * @since 1.2.2.7 Even if the menu item was specified to be hidden, when it is the context page, then show the submenu item so the user will have the right context page. * * @param string $slug * @param bool $is_tabs_visibility_check This is used to decide if the associated tab should be shown or hidden. * * @return bool */ function is_submenu_item_visible( $slug, $is_tabs_visibility_check = false ) { if ( $this->is_admin_page( $slug ) ) { /** * It is the current context page, so show the submenu item * so the user will have the right context page, even if it * was set to hidden. */ return true; } if ( ! $this->has_settings_menu() ) { // No menu settings at all. return false; } if ( ! $is_tabs_visibility_check && $this->is_org_repo_compliant() && $this->show_settings_with_tabs() ) { /** * wp.org themes are limited to a single submenu item, and * sub-submenu items are most likely not allowed (never verified). */ return false; } return $this->_menu->is_submenu_item_visible( $slug ); } /** * Check if a Freemius page should be accessible via the UI. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @param string $slug * * @return bool */ function is_page_visible( $slug ) { if ( $this->is_admin_page( $slug ) ) { return true; } return $this->_menu->is_submenu_item_visible( $slug, true, true ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 */ private function _version_updates_handler() { if ( ! isset( $this->_storage->sdk_version ) || $this->_storage->sdk_version != $this->version ) { // Freemius version upgrade mode. $this->_storage->sdk_last_version = $this->_storage->sdk_version; $this->_storage->sdk_version = $this->version; if ( empty( $this->_storage->sdk_last_version ) || version_compare( $this->_storage->sdk_last_version, $this->version, '<' ) ) { $this->_storage->sdk_upgrade_mode = true; $this->_storage->sdk_downgrade_mode = false; } else { $this->_storage->sdk_downgrade_mode = true; $this->_storage->sdk_upgrade_mode = false; } $this->do_action( 'sdk_version_update', $this->_storage->sdk_last_version, $this->version ); } $plugin_version = $this->get_plugin_version(); if ( ! isset( $this->_storage->plugin_version ) || $this->_storage->plugin_version != $plugin_version ) { // Plugin version upgrade mode. $this->_storage->plugin_last_version = $this->_storage->plugin_version; $this->_storage->plugin_version = $plugin_version; if ( empty( $this->_storage->plugin_last_version ) || version_compare( $this->_storage->plugin_last_version, $plugin_version, '<' ) ) { $this->_storage->plugin_upgrade_mode = true; $this->_storage->plugin_downgrade_mode = false; } else { $this->_storage->plugin_downgrade_mode = true; $this->_storage->plugin_upgrade_mode = false; } if ( ! empty( $this->_storage->plugin_last_version ) ) { // Different version of the plugin was installed before, therefore it's an update. $this->_storage->is_plugin_new_install = false; } $this->do_action( 'plugin_version_update', $this->_storage->plugin_last_version, $plugin_version ); } } #-------------------------------------------------------------------------------- #region Data Migration on SDK Update #-------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 1.1.5 * * @param string $sdk_prev_version * @param string $sdk_version */ function _sdk_version_update( $sdk_prev_version, $sdk_version ) { if ( empty( $sdk_prev_version ) ) { return; } if ( version_compare( $sdk_prev_version, '2.5.1', '<' ) && version_compare( $sdk_version, '2.5.1', '>=' ) ) { if ( $this->is_registered( true ) ) { /** * Migrate to new permissions layer. */ require_once WP_FS__DIR_INCLUDES . '/supplements/fs-migration-2.5.1.php'; $install_by_blog_id = is_multisite() ? $this->get_blog_install_map() : array( 0 => $this->_site ); fs_migrate_251( $this, $install_by_blog_id ); } } } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param \FS_Storage $storage * @param bool|int|null $blog_id */ private static function migrate_install_plan_to_plan_id( FS_Storage $storage, $blog_id = null ) { if ( empty( $storage->sdk_version ) ) { // New installation of the plugin, no need to upgrade. return; } if ( ! version_compare( $storage->sdk_version, '2.0.0', '<' ) ) { // Previous version is >= 2.0.0, so no need to migrate. return; } // Alias. $module_type = $storage->get_module_type(); $module_slug = $storage->get_module_slug(); $installs = self::get_all_sites( $module_type, $blog_id ); $install = isset( $installs[ $module_slug ] ) ? $installs[ $module_slug ] : null; if ( ! is_object( $install ) ) { return; } if ( isset( $install->plan ) && is_object( $install->plan ) ) { if ( isset( $install->plan->id ) && ! empty( $install->plan->id ) ) { $install->plan_id = self::_decrypt( $install->plan->id ); } unset( $install->plan ); $installs[ $module_slug ] = clone $install; self::set_account_option_by_module( $module_type, 'sites', $installs, true, $blog_id ); } } /** * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @param string $plugin_prev_version * @param string $plugin_version */ function _after_version_update( $plugin_prev_version, $plugin_version ) { if ( $this->is_theme() ) { // Expire the cache of the previous tabs since the theme may // have setting updates. $this->_cache->expire( 'tabs' ); $this->_cache->expire( 'tabs_stylesheets' ); } } /** * A special migration logic for the $_accounts, executed for all the plugins in the system: * - Moves some data to the network level storage. * - If the plugin's connection was skipped for all sites, set the plugin as if it was network skipped. * - If the plugin's connection was ignored for all sites, don't do anything in terms of the network connection. * - If the plugin was connected to all sites by the same super-admin, set the plugin as if was network opted-in for all sites. * - If there's at least one site that was connected by a super-admin, find the "main super-admin" (the one that installed the majority of the plugin installs) and set the plugin as if was network activated with the main super-admin, set all the sites that were skipped or opted-in with a different user to delegated mode. Then, prompt the currently logged super-admin to choose what to do with the ignored sites. * - If there are any sites in the network which the connection decision was not yet taken for, set this plugin into network activation mode so a super-admin can choose what to do with the rest of the sites. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ private static function migrate_accounts_to_network() { $sites = self::get_sites(); $sites_count = count( $sites ); $connection_status = array(); $plugin_slugs = array(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); self::$_accounts->migrate_to_network( $blog_id ); /** * Build a list of all Freemius powered plugins slugs. */ $id_slug_type_path_map = self::$_accounts->get_option( 'id_slug_type_path_map', array(), $blog_id ); foreach ( $id_slug_type_path_map as $module_id => $data ) { if ( WP_FS__MODULE_TYPE_PLUGIN === $data['type'] ) { $plugin_slugs[ $data['slug'] ] = true; } } $installs = self::get_account_option( 'sites', WP_FS__MODULE_TYPE_PLUGIN, $blog_id ); if ( is_array( $installs ) ) { foreach ( $installs as $slug => $install ) { if ( ! isset( $connection_status[ $slug ] ) ) { $connection_status[ $slug ] = array(); } if ( is_object( $install ) && FS_Site::is_valid_id( $install->id ) && FS_User::is_valid_id( $install->user_id ) ) { $connection_status[ $slug ][ $blog_id ] = $install->user_id; } } } } foreach ( $plugin_slugs as $slug => $true ) { if ( ! isset( $connection_status[ $slug ] ) ) { $connection_status[ $slug ] = array(); } foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); if ( isset( $connection_status[ $slug ][ $blog_id ] ) ) { continue; } $storage = FS_Storage::instance( WP_FS__MODULE_TYPE_PLUGIN, $slug ); $is_anonymous = $storage->get( 'is_anonymous', null, $blog_id ); if ( ! is_null( $is_anonymous ) ) { // Since 1.1.3 is_anonymous is an array. if ( is_array( $is_anonymous ) && isset( $is_anonymous['is'] ) ) { $is_anonymous = $is_anonymous['is']; } if ( is_bool( $is_anonymous ) && true === $is_anonymous ) { $connection_status[ $slug ][ $blog_id ] = 'skipped'; } } if ( ! isset( $connection_status[ $slug ][ $blog_id ] ) ) { $connection_status[ $slug ][ $blog_id ] = 'ignored'; } } } $super_admins = array(); foreach ( $connection_status as $slug => $blogs_status ) { $skips = 0; $ignores = 0; $connections = 0; $opted_in_users = array(); $opted_in_super_admins = array(); $storage = FS_Storage::instance( WP_FS__MODULE_TYPE_PLUGIN, $slug ); foreach ( $blogs_status as $blog_id => $status_or_user_id ) { if ( 'skipped' === $status_or_user_id ) { $skips ++; } else if ( 'ignored' === $status_or_user_id ) { $ignores ++; } else if ( FS_User::is_valid_id( $status_or_user_id ) ) { $connections ++; if ( ! isset( $opted_in_users[ $status_or_user_id ] ) ) { $opted_in_users[ $status_or_user_id ] = array(); } $opted_in_users[ $status_or_user_id ][] = $blog_id; if ( isset( $super_admins[ $status_or_user_id ] ) || self::is_super_admin( $status_or_user_id ) ) { // Cache super-admin data. $super_admins[ $status_or_user_id ] = true; // Remember opted-in super-admins for the plugin. $opted_in_super_admins[ $status_or_user_id ] = true; } } } $main_super_admin_user_id = null; $all_migrated = false; if ( $sites_count == $skips ) { // All sites were skipped -> network skip by copying the anonymous mode from any of the sites. $storage->is_anonymous_ms = $storage->is_anonymous; $all_migrated = true; } else if ( $sites_count == $ignores ) { // Don't do anything, still in activation mode. $all_migrated = true; } else if ( 0 < count( $opted_in_super_admins ) ) { // Find the super-admin with the majority of installs. $max_installs_by_super_admin = 0; foreach ( $opted_in_super_admins as $user_id => $true ) { $installs_count = count( $opted_in_users[ $user_id ] ); if ( $installs_count > $max_installs_by_super_admin ) { $max_installs_by_super_admin = $installs_count; $main_super_admin_user_id = $user_id; } } if ( $sites_count == $connections && 1 == count( $opted_in_super_admins ) ) { // Super-admin opted-in for all sites in the network. $storage->is_network_connected = true; $all_migrated = true; } // Store network user. $storage->network_user_id = $main_super_admin_user_id; $storage->network_install_blog_id = ( $sites_count == $connections ) ? // Since all sites are opted-in, associating with the main site. get_current_blog_id() : // Associating with the 1st found opted-in site. $opted_in_users[ $main_super_admin_user_id ][0]; /** * Make sure we migrate the plan ID of the network install, otherwise, if after the migration * the 1st page that will be loaded is the network level WP Admin and $storage->network_install_blog_id * is different than the main site of the network, the $this->_site will not be set since the plan_id * will be empty. */ $storage->migrate_to_network(); self::migrate_install_plan_to_plan_id( $storage, $storage->network_install_blog_id ); } else { // At least one opt-in. All the opt-in were created by a non-super-admin. if ( 0 == $ignores ) { // All sites were opted-in or skipped, all by non-super-admin. So delegate all. $storage->store( 'is_delegated_connection', true, true ); $all_migrated = true; } } if ( ! $all_migrated ) { /** * Delegate all sites that were: * 1) Opted-in by a user that is NOT the main-super-admin. * 2) Skipped and non of the sites was opted-in by a super-admin. If any site was opted-in by a super-admin, there will be a main-super-admin, and we consider the skip as if it was done by that user. */ foreach ( $blogs_status as $blog_id => $status_or_user_id ) { if ( $status_or_user_id == $main_super_admin_user_id ) { continue; } if ( FS_User::is_valid_id( $status_or_user_id ) || ( 'skipped' === $status_or_user_id && is_null( $main_super_admin_user_id ) ) ) { $storage->store( 'is_delegated_connection', true, $blog_id ); } } } if ( ( $connections + $skips > 0 ) ) { if ( $ignores > 0 ) { /** * If admin already opted-in or skipped in any of the network sites, and also * have sites which the connection decision was not yet taken, set this plugin * into network activation mode so the super-admin can choose what to do with * the rest of the sites. */ self::set_network_upgrade_mode( $storage ); } } } } /** * Set a module into network upgrade mode. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_Storage $storage * * @return bool */ public static function set_network_upgrade_mode( FS_Storage $storage ) { return $storage->is_network_activation = true; } /** * Will return true after upgrading to the SDK with the network level integration, * when the super-admin involvement is required regarding the rest of the sites. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return bool */ function is_network_upgrade_mode() { return $this->_storage->get( 'is_network_activation' ); } /** * Clear flag after the upgrade mode completion. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return bool True if network activation was on and now completed. */ private function network_upgrade_mode_completed() { if ( fs_is_network_admin() && $this->is_network_upgrade_mode() ) { $this->_storage->remove( 'is_network_activation' ); return true; } return false; } #endregion /** * This action is connected to the 'plugins_loaded' hook and helps to determine * if this is a new plugin installation or a plugin update. * * There are 3 different use-cases: * 1) New plugin installation right with Freemius: * 1.1 _activate_plugin_event_hook() will be executed first * 1.2 Since $this->_storage->is_plugin_new_install is not set, * and $this->_storage->plugin_last_version is not set, * $this->_storage->is_plugin_new_install will be set to TRUE. * 1.3 When _plugins_loaded() will be executed, $this->_storage->is_plugin_new_install will * be already set to TRUE. * * 2) Plugin update, didn't have Freemius before, and now have the SDK: * 2.1 _activate_plugin_event_hook() will not be executed, because * the activation hook do NOT fires on updates since WP 3.1. * 2.2 When _plugins_loaded() will be executed, $this->_storage->is_plugin_new_install will * be empty, therefore, it will be set to FALSE. * * 3) Plugin update, had Freemius in prev version as well: * 3.1 _version_updates_handler() will be executed 1st, since FS was installed * before, $this->_storage->plugin_last_version will NOT be empty, * therefore, $this->_storage->is_plugin_new_install will be set to FALSE. * 3.2 When _plugins_loaded() will be executed, $this->_storage->is_plugin_new_install is * already set, therefore, it will not be modified. * * Use-case #3 is backward compatible, #3.1 will be executed since 1.0.9. * * NOTE: * The only fallback of this mechanism is if an admin updates a plugin based on use-case #2, * and then, the next immediate PageView is the plugin's main settings page, it will not * show the opt-in right away. The reason it will happen is because Freemius execution * will be turned off till the plugin is fully loaded at least once * (till $this->_storage->was_plugin_loaded is TRUE). * * @author Vova Feldman (@svovaf) * @since 1.1.9 * */ function _plugins_loaded() { // Update flag that plugin was loaded with Freemius at least once. $this->_storage->was_plugin_loaded = true; /** * Bug fix - only set to false when it's a plugin, due to the * execution sequence of the theme hooks and our methods, if * this will be set for themes, Freemius will always assume * it's a theme update. * * @author Vova Feldman (@svovaf) * @since 1.2.2.2 */ if ( $this->is_plugin() && ! isset( $this->_storage->is_plugin_new_install ) ) { $this->_storage->is_plugin_new_install = ( ! is_plugin_active( $this->_plugin_basename ) && empty( $this->_storage->plugin_last_version ) ); } } function _run_garbage_collector() { if ( true !== fs_get_optional_constant( 'WP_FS__ENABLE_GARBAGE_COLLECTOR', true ) ) { return; } if ( ! $this->is_user_in_admin() ) { return; } require_once WP_FS__DIR_INCLUDES . '/class-fs-lock.php'; $lock = new FS_Lock( 'garbage_collection' ); if ( $lock->is_locked() ) { return; } // Create a 1-day lock. $lock->lock( WP_FS__TIME_24_HOURS_IN_SEC ); FS_Garbage_Collector::instance()->clean(); } /** * Modifies all external links in the submenu by altering their href, and also opens them in new tab if needed. * * @author Vova Feldman (@svovaf) * @author Swashata Ghosh (@swashata) * @since 2.1.4 */ static function _handle_submenu_external_link() { ?> _logger->entrance(); if ( is_admin() ) { add_action( 'admin_init', array( &$this, '_hook_action_links_and_register_account_hooks' ) ); if ( $this->is_plugin() ) { if ( self::is_plugin_install_page() && true !== fs_request_get_bool( 'fs_allow_updater_and_dialog' ) ) { /** * Unless the `fs_allow_updater_and_dialog` URL param exists and its value is `true`, make * Freemius-related updates unavailable on the "Add Plugins" admin page (/plugin-install.php) * so that they won't interfere with the .org plugins' functionalities on that page (e.g. * updating of a .org plugin). */ add_filter( 'site_transient_update_plugins', array( 'Freemius', '_remove_fs_updates_from_plugin_install_page' ), 10, 2 ); } else if ( self::is_plugins_page() || self::is_updates_page() ) { /** * On the "Plugins" and "Updates" admin pages, if there are premium or non–org-compliant plugins, modify their details dialog URLs (add a Freemius-specific param) so that the SDK can determine if the plugin information dialog should show information from Freemius. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ add_action( 'admin_footer', array( 'Freemius', '_prepend_fs_allow_updater_and_dialog_flag_url_param' ) ); } $plugin_dir = dirname( $this->_plugin_dir_path ) . '/'; /** * @since 1.2.2 * * Hook to both free and premium version activations to support * auto deactivation on the other version activation. */ register_activation_hook( $plugin_dir . $this->_free_plugin_basename, array( &$this, '_activate_plugin_event_hook' ) ); register_activation_hook( $plugin_dir . $this->premium_plugin_basename(), array( &$this, '_activate_plugin_event_hook' ) ); } else { add_action( 'after_switch_theme', array( &$this, '_activate_theme_event_hook' ), 10, 2 ); add_action( 'admin_footer', array( &$this, '_style_premium_theme' ) ); } /** * Part of the mechanism to identify new plugin install vs. plugin update. * * @author Vova Feldman (@svovaf) * @since 1.1.9 */ if ( empty( $this->_storage->was_plugin_loaded ) ) { /** * During the plugin activation (not theme), 'plugins_loaded' will be already executed * when the logic gets here since the activation logic first add the activate plugins, * then triggers 'plugins_loaded', and only then include the code of the plugin that * is activated. Which means that _plugins_loaded() will NOT be executed during the * plugin activation, and that IS intentional. * * @author Vova Feldman (@svovaf) */ if ( $this->is_plugin() && $this->is_activation_mode( false ) && 0 == did_action( 'plugins_loaded' ) ) { add_action( 'plugins_loaded', array( &$this, '_plugins_loaded' ) ); } else { // If was activated before, then it was already loaded before. $this->_plugins_loaded(); } } add_action( 'plugins_loaded', array( &$this, '_run_garbage_collector' ) ); if ( ! self::is_ajax() ) { if ( ! $this->is_addon() ) { add_action( 'init', array( &$this, '_add_default_submenu_items' ), WP_FS__LOWEST_PRIORITY ); } } if ( $this->_storage->handle_gdpr_admin_notice ) { add_action( 'init', array( &$this, '_maybe_show_gdpr_admin_notice' ) ); } add_action( 'init', array( &$this, '_maybe_add_gdpr_optin_ajax_handler') ); add_action( 'init', array( &$this, '_add_pricing_ajax_handler' ) ); } if ( $this->is_plugin() ) { if ( version_compare( $GLOBALS['wp_version'], '5.1', '<' ) ) { add_action( 'wpmu_new_blog', array( $this, '_after_new_blog_callback' ), 10, 6 ); } else { add_action( 'wp_initialize_site', array( $this, '_after_wp_initialize_site_callback' ), 11, 2 ); } register_deactivation_hook( $this->_plugin_main_file_path, array( &$this, '_deactivate_plugin_hook' ) ); } if ( is_multisite() ) { add_action( 'deactivate_blog', array( &$this, '_after_site_deactivated_callback' ) ); add_action( 'archive_blog', array( &$this, '_after_site_deactivated_callback' ) ); add_action( 'make_spam_blog', array( &$this, '_after_site_deactivated_callback' ) ); if ( version_compare( $GLOBALS['wp_version'], '5.1', '<' ) ) { add_action( 'deleted_blog', array( $this, '_after_site_deleted_callback' ), 10, 2 ); } else { add_action( 'wp_delete_site', array( $this, '_after_wpsite_deleted_callback' ) ); } add_action( 'activate_blog', array( &$this, '_after_site_reactivated_callback' ) ); add_action( 'unarchive_blog', array( &$this, '_after_site_reactivated_callback' ) ); add_action( 'make_ham_blog', array( &$this, '_after_site_reactivated_callback' ) ); } if ( $this->is_theme() && self::is_customizer() && $this->apply_filters( 'show_customizer_upsell', true ) ) { // Register customizer upsell. add_action( 'customize_register', array( &$this, '_customizer_register' ) ); } add_action( 'admin_init', array( &$this, '_redirect_on_clicked_menu_link' ), WP_FS__LOWEST_PRIORITY ); if ( $this->is_theme() && ! $this->is_migration() ) { add_action( 'admin_init', array( &$this, '_add_tracking_links' ) ); } add_action( 'admin_init', array( &$this, '_add_license_activation' ) ); add_action( 'admin_init', array( &$this, '_add_premium_version_upgrade_selection' ) ); add_action( 'admin_init', array( &$this, '_add_beta_mode_update_handler' ) ); add_action( 'admin_init', array( &$this, '_add_user_change_option' ) ); add_action( 'admin_init', array( &$this, '_add_email_address_update_option' ) ); $this->add_ajax_action( 'update_billing', array( &$this, '_update_billing_ajax_action' ) ); $this->add_ajax_action( 'start_trial', array( &$this, '_start_trial_ajax_action' ) ); $this->add_ajax_action( 'set_data_debug_mode', array( &$this, '_set_data_debug_mode' ) ); $this->add_ajax_action( 'toggle_whitelabel_mode', array( &$this, '_toggle_whitelabel_mode_ajax_handler' ) ); if ( $this->_is_network_active && fs_is_network_admin() ) { $this->add_ajax_action( 'network_activate', array( &$this, '_network_activate_ajax_action' ) ); } $this->add_ajax_action( 'install_premium_version', array( &$this, '_install_premium_version_ajax_action' ) ); $this->add_ajax_action( 'submit_affiliate_application', array( &$this, '_submit_affiliate_application' ) ); $this->add_action( 'after_plans_sync', array( &$this, '_check_for_trial_plans' ) ); $this->add_action( 'sdk_version_update', array( &$this, '_sdk_version_update' ), WP_FS__DEFAULT_PRIORITY, 2 ); $this->add_action( 'plugin_version_update', array( &$this, '_after_version_update' ), WP_FS__DEFAULT_PRIORITY, 2 ); $this->add_filter( 'after_code_type_change', array( &$this, '_after_code_type_change' ) ); add_action( 'admin_init', array( &$this, '_add_trial_notice' ) ); // @phpstan-ignore-line add_action( 'admin_init', array( &$this, '_add_affiliate_program_notice' ) ); // @phpstan-ignore-line add_action( 'admin_enqueue_scripts', array( &$this, '_enqueue_common_css' ) ); /** * Handle request to reset anonymous mode for `get_reconnect_url()` or reset the pending activation mode. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 */ if ( ( fs_request_is_action( 'reset_anonymous_mode' ) || fs_request_is_action( 'reset_pending_activation_mode' ) ) && $this->get_unique_affix() === fs_request_get_raw( 'fs_unique_affix' ) ) { add_action( 'admin_init', array( &$this, 'connect_again' ) ); } FS_DebugManager::register_hooks(); } /** * Register the required hooks right after the settings parse is completed. * * @author Vova Feldman (@svovaf) * @since 2.3.1 */ private function register_after_settings_parse_hooks() { if ( is_admin() && $this->is_theme() && $this->is_premium() && ! $this->has_active_valid_license() ) { $this->add_ajax_action( 'delete_theme_update_data', array( &$this, '_delete_theme_update_data_action' ) ); } if ( $this->show_settings_with_tabs() ) { /** * Include the required hooks to capture the theme settings' page tabs * and cache them. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ if ( ! $this->_cache->has_valid( 'tabs' ) ) { add_action( 'admin_footer', array( &$this, '_tabs_capture' ) ); // Add license activation AJAX callback. $this->add_ajax_action( 'store_tabs', array( &$this, '_store_tabs_ajax_action' ) ); add_action( 'admin_enqueue_scripts', array( &$this, '_store_tabs_styles' ), 9999999 ); } add_action( 'admin_footer', array( &$this, '_add_freemius_tabs' ), /** * The tabs JS code must be executed after the tabs capture logic (_tabs_capture()). * That's why the priority is 11 while the tabs capture logic is added * with priority 10. * * @author Vova Feldman (@svovaf) */ 11 ); } if ( ! self::is_ajax() ) { if ( ! $this->is_addon() || $this->is_only_premium() ) { add_action( ( $this->_is_network_active && fs_is_network_admin() ? 'network_' : '' ) . 'admin_menu', array( &$this, '_prepare_admin_menu' ), WP_FS__LOWEST_PRIORITY ); } } if ( $this->is_user_in_admin() && $this->is_parallel_activation() && $this->_premium_plugin_basename !== $this->_premium_plugin_basename_from_parallel_activation ) { $this->_premium_plugin_basename = $this->_premium_plugin_basename_from_parallel_activation; register_activation_hook( dirname( $this->_plugin_dir_path ) . '/' . $this->_premium_plugin_basename, array( &$this, '_activate_plugin_event_hook' ) ); } } /** * Determines if a plugin is running in parallel activation mode. * * @author Leo Fajardo (@leorw) * @since 2.9.1 * * @return bool */ private function is_parallel_activation() { return ! empty( $this->_premium_plugin_basename_from_parallel_activation ); } /** * Makes Freemius-related updates unavailable on the "Add Plugins" admin page (/plugin-install.php) so that * they won't interfere with the .org plugins' functionalities on that page (e.g. updating of a .org plugin). * * @author Leo Fajardo (@leorw) * @since 2.2.3 * * @param object $updates * @param string|null $transient * * @return object */ static function _remove_fs_updates_from_plugin_install_page( $updates, $transient = null ) { if ( is_object( $updates ) && isset( $updates->response ) ) { foreach ( $updates->response as $file => $plugin ) { if ( isset( $plugin->package ) && false !== strpos( $plugin->package, 'api.freemius' ) ) { unset( $updates->response[ $file ] ); } } } return $updates; } /** * Prepends the `fs_allow_updater_and_dialog` param to the plugin information URLs to tell the SDK to handle * the information that is shown on the plugin details dialog that is shown when the relevant link is clicked. * * @author Leo Fajardo (@leorw) * @since 2.2.3 * * @return void */ static function _prepend_fs_allow_updater_and_dialog_flag_url_param() { $slug_basename_map = array(); foreach ( self::$_instances as $instance ) { if ( ! $instance->is_plugin() ) { continue; } $slug_basename_map[ $instance->get_slug() ] = $instance->premium_plugin_basename(); } ?> is_beta() ) { $has_any_beta_version = true; break; } } if ( $has_any_beta_version ) { fs_enqueue_local_style( 'fs_plugins', '/admin/plugins.css' ); } } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 */ static function _maybe_add_beta_label_to_plugins_and_handle_confirmation() { $beta_data = array(); foreach ( self::$_instances as $instance ) { if ( ! $instance->is_premium() ) { continue; } /** * If there's an available beta version update, a confirmation message will be shown when the * "Update now" link on the "Plugins" or "Themes" page is clicked. */ $has_beta_update = $instance->has_beta_update(); $is_beta = ( // The "Beta" label is added separately for themes. $instance->is_plugin() && $instance->is_beta() ); if ( ! $is_beta && ! $has_beta_update ) { continue; } $beta_data[ $instance->get_plugin_basename() ] = array( 'is_installed_version_beta' => $is_beta ); if ( ! $has_beta_update ) { continue; } $beta_data[ $instance->get_plugin_basename() ]['beta_version_update_confirmation_message'] = sprintf( '%s %s', sprintf( fs_esc_attr_inline( 'An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned.', 'beta-version-update-caution', $instance->get_slug() ), $instance->get_plugin_title() ), fs_esc_attr_inline( 'Would you like to proceed with the update?', 'update-confirmation', $instance->get_slug() ) ); } if ( empty( $beta_data ) ) { return; } ?> _free_plugin_basename ] ); unset( $uninstallable_plugins[ $this->premium_plugin_basename() ] ); update_option( 'uninstall_plugins', $uninstallable_plugins ); } /** * @since 1.2.0 Invalidate module's main file cache, otherwise, FS_Plugin_Updater will not fetch updates. * * @param bool $store_prev_path */ private function clear_module_main_file_cache( $store_prev_path = true ) { if ( ! isset( $this->_storage->plugin_main_file ) || empty( $this->_storage->plugin_main_file->path ) ) { return; } if ( ! $store_prev_path ) { /** * Storing the previous path is not needed when clearing the cache after an SDK version update since * the main purpose of the cache clearing in that event is to correct a wrong plugin main file path * which causes data mix-up between plugins (e.g. titles and versions of an add-on and its parent plugin). * * @author Leo Fajardo (@leorw) * @since 2.2.1 */ unset( $this->_storage->plugin_main_file->path ); } else { $plugin_main_file = clone $this->_storage->plugin_main_file; // Store cached path (2nd layer cache). $plugin_main_file->prev_path = $plugin_main_file->path; // Clear cached path. unset( $plugin_main_file->path ); $this->_storage->plugin_main_file = $plugin_main_file; } /** * Clear global cached path. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ $id_slug_type_path_map = self::$_accounts->get_option( 'id_slug_type_path_map' ); unset( $id_slug_type_path_map[ $this->_module_id ]['path'] ); self::$_accounts->set_option( 'id_slug_type_path_map', $id_slug_type_path_map, true ); } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 */ function _hook_action_links_and_register_account_hooks() { if ( $this->is_migration() ) { return; } if ( ( self::is_plugins_page() && $this->is_plugin() ) || ( self::is_themes_page() && $this->is_theme() ) || fs_request_is_action_secure( $this->get_unique_affix() . '_reconnect' ) ) { $this->_add_tracking_links(); } if ( self::is_plugins_page() && $this->is_plugin() ) { $this->hook_plugin_action_links(); } $this->_register_account_hooks(); } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 */ private function _register_account_hooks() { if ( ! is_admin() ) { return; } /** * Always show the deactivation feedback form since we added * automatic free version deactivation upon premium code activation. * * @since 1.2.1.6 */ $this->add_ajax_action( 'submit_uninstall_reason', array( &$this, '_submit_uninstall_reason_action' ) ); $this->add_ajax_action( 'cancel_subscription_or_trial', array( &$this, 'cancel_subscription_or_trial_ajax_action' ) ); if ( ! $this->is_addon() || $this->is_parent_plugin_installed() ) { if ( ( $this->is_plugin() && self::is_plugins_page() ) || ( $this->is_theme() && self::is_themes_page() ) ) { add_action( 'admin_footer', array( &$this, '_add_deactivation_feedback_dialog_box' ) ); } } } /** * Leverage backtrace to find caller plugin file path. * * @param bool $is_init Is initiation sequence. * @param string $main_file Since 2.5.0 expects the module's main file path to potentially purge the cached path. * * @return string * @since 1.0.6 * * @author Vova Feldman (@svovaf) */ private function _find_caller_plugin_file( $is_init = false, $main_file = '' ) { // Try to load the cached value of the file path. if ( isset( $this->_storage->plugin_main_file ) ) { $plugin_main_file = $this->_storage->plugin_main_file; if ( ! empty( $plugin_main_file->path ) ) { $absolute_path = $this->get_absolute_path( $plugin_main_file->path ); if ( file_exists( $absolute_path ) ) { if ( $is_init && $absolute_path !== $this->get_absolute_path( $main_file ) ) { // Update cached path if not matching the actual path. $plugin_main_file->path = $main_file; $this->_storage->plugin_main_file = $plugin_main_file; } return $absolute_path; } } } /** * @since 1.2.1 * * `clear_module_main_file_cache()` is clearing the plugin's cached path on * deactivation. Therefore, if any plugin/theme was initiating `Freemius` * with that plugin's slug, it was overriding the empty plugin path with a wrong path. * * So, we've added a special mechanism with a 2nd layer of cache that uses `prev_path` * when the class instantiator isn't the module. */ if ( ! $is_init ) { // Fetch prev path cache. if ( isset( $this->_storage->plugin_main_file ) && ! empty( $this->_storage->plugin_main_file->prev_path ) ) { $absolute_path = $this->get_absolute_path( $this->_storage->plugin_main_file->prev_path ); if ( file_exists( $absolute_path ) ) { return $absolute_path; } } wp_die( $this->get_text_inline( 'Freemius SDK couldn\'t find the plugin\'s main file. Please contact sdk@freemius.com with the current error.', 'failed-finding-main-path' ) . " Module: {$this->_slug}; SDK: " . WP_FS__SDK_VERSION . ";", $this->get_text_inline( 'Error', 'error' ), array( 'back_link' => true ) ); } /** * @since 1.2.1 * * Only the original instantiator that calls dynamic_init can modify the module's path. */ // Find caller module. $this->_storage->plugin_main_file = (object) array( 'path' => $main_file, ); return $this->get_absolute_path( $main_file ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 * * @param string $path * * @return string */ private function get_relative_path( $path ) { $module_root_dir = $this->get_module_root_dir_path(); if ( 0 === strpos( $path, $module_root_dir ) ) { $path = substr( $path, strlen( $module_root_dir ) ); } return $path; } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 * * @param string $path * @param string|bool $module_type * * @return string */ private function get_absolute_path( $path, $module_type = false ) { $module_root_dir = $this->get_module_root_dir_path( $module_type ); if ( 0 !== strpos( $path, $module_root_dir ) ) { $path = fs_normalize_path( $module_root_dir . $path ); } return $path; } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 * * @param string|bool $module_type * * @return string */ private function get_module_root_dir_path( $module_type = false ) { $is_plugin = empty( $module_type ) ? $this->is_plugin() : ( WP_FS__MODULE_TYPE_PLUGIN === $module_type ); return fs_normalize_path( trailingslashit( $is_plugin ? WP_PLUGIN_DIR : get_theme_root( get_stylesheet() ) ) ); } /** * @author Leo Fajardo (@leorw) * * @param number $module_id * @param string $slug * * @return string Since 2.5.0 return the module's main file path. * * @since 1.2.2 */ private function store_id_slug_type_path_map( $module_id, $slug ) { $id_slug_type_path_map = self::$_accounts->get_option( 'id_slug_type_path_map', array() ); $store_option = false; if ( ! isset( $id_slug_type_path_map[ $module_id ] ) ) { $id_slug_type_path_map[ $module_id ] = array( 'slug' => $slug ); $store_option = true; } else if ( isset( $id_slug_type_path_map[ $module_id ]['slug'] ) && $slug !== $id_slug_type_path_map[ $module_id ]['slug'] ) { $id_slug_type_path_map[ $module_id ]['slug'] = $slug; $store_option = true; } $find_caller = empty( $id_slug_type_path_map[ $module_id ]['path'] ); if ( ! $find_caller ) { /** * This verification is for cases when suddenly the same module * is installed but with a different folder name. * * @author Vova Feldman (@svovaf) * @since 1.2.3 */ $find_caller = ! file_exists( $this->get_absolute_path( $id_slug_type_path_map[ $module_id ]['path'], $id_slug_type_path_map[ $module_id ]['type'] ) ); } foreach ( $id_slug_type_path_map as $id => $data ) { if ( empty( $id ) ) { // Remove maps with empty module ID. unset( $id_slug_type_path_map[ $id ] ); $store_option = true; continue; } /** * If the module's main file path is identical to the main file path of another module then it means that the cached path of the current module or the other one with the same path is wrong, and therefore, we need to recalculate those paths. * * @author Vova Feldman (@svovaf) * @since 2.5.0 */ if ( ! $find_caller ) { if ( $id == $module_id ) { continue; } if ( isset( $data['path'] ) && $data['path'] === $id_slug_type_path_map[ $module_id ]['path'] ) { $find_caller = true; } } } if ( $find_caller ) { $caller_main_file_and_type = $this->get_caller_main_file_and_type( $module_id ); $id_slug_type_path_map[ $module_id ]['type'] = $caller_main_file_and_type->module_type; $id_slug_type_path_map[ $module_id ]['path'] = $caller_main_file_and_type->path; $store_option = true; } if ( $store_option ) { self::$_accounts->set_option( 'id_slug_type_path_map', $id_slug_type_path_map, true ); } return $id_slug_type_path_map[ $module_id ]['path']; } /** * Identifies the caller type: plugin or theme. * * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @author Vova Feldman (@svovaf) * @since 1.2.2.3 Find the earliest module in the call stack that calls to the SDK. This fix is for cases when * add-ons are relying on loading the SDK from the parent module, and also allows themes including the * SDK an internal file instead of directly from functions.php. * @since 1.2.1.7 Knows how to handle cases when an add-on includes the parent module logic. * * @param number $module_id @since 2.5.0 */ private function get_caller_main_file_and_type( $module_id ) { self::require_plugin_essentials(); $all_plugins = fs_get_plugins( true ); $all_plugins_paths = array(); // Get active plugin's main files real full names (might be symlinks). foreach ( $all_plugins as $relative_path => $data ) { if ( false === strpos( fs_normalize_path( $relative_path ), '/' ) ) { /** * Ignore plugins that don't have a folder (e.g. Hello Dolly) since they * can't really include the SDK. * * @author Vova Feldman * @since 1.2.1.7 */ continue; } $all_plugins_paths[] = fs_normalize_path( realpath( WP_PLUGIN_DIR . '/' . $relative_path ) ); } $caller_file_candidate = false; $caller_map = array(); $module_type = WP_FS__MODULE_TYPE_PLUGIN; $themes_dir = fs_normalize_path( get_theme_root( get_stylesheet() ) ); $plugin_dir_to_skip = false; for ( $i = 1, $bt = debug_backtrace(), $len = count( $bt ); $i < $len; $i ++ ) { if ( empty( $bt[ $i ]['file'] ) ) { continue; } if ( $i > 1 && ! empty( $bt[ $i - 1 ]['file'] ) && $bt[ $i ]['file'] === $bt[ $i - 1 ]['file'] ) { // If file same as the prev file in the stack, skip it. continue; } if ( ! empty( $bt[ $i ]['function'] ) && in_array( $bt[ $i ]['function'], array( 'do_action', 'apply_filter', // The string split is stupid, but otherwise, theme check // throws info notices. 'requir' . 'e_once', 'requir' . 'e', 'includ' . 'e_once', 'includ' . 'e', 'install_and_activate_plugin', 'try_activate_plugin', 'activate_plugin' ) ) ) { if ( 'activate_plugin' === $bt[ $i ]['function'] ) { /** * Store the directory of the activator plugin so that any other file that starts with it * cannot be mistakenly chosen as a candidate caller file. * * @author Leo Fajardo * * @since 2.3.0 */ $caller_file_path = fs_normalize_path( $bt[ $i ]['file'] ); foreach ( $all_plugins_paths as $plugin_path ) { $plugin_dir = fs_normalize_path( dirname( $plugin_path ) . '/' ); if ( false !== strpos( $caller_file_path, $plugin_dir ) ) { $plugin_dir_to_skip = $plugin_dir; break; } } } // Ignore call stack hooks and files inclusion. continue; } $caller_file_path = fs_normalize_path( $bt[ $i ]['file'] ); if ( ! empty( $plugin_dir_to_skip ) ) { /** * Skip if it's an activator plugin file to avoid mistakenly choosing it as a candidate caller file. * * @author Leo Fajardo * * @since 2.3.0 */ if ( 0 === strpos( $caller_file_path, $plugin_dir_to_skip ) ) { continue; } } if ( 'functions.php' === basename( $caller_file_path ) ) { /** * 1. Assumes that theme's starting execution file is functions.php. * 2. This complex logic fixes symlink issues (e.g. with Vargant). * * @author Vova Feldman (@svovaf) * @since 1.2.2.5 */ if ( $caller_file_path == fs_normalize_path( realpath( trailingslashit( $themes_dir ) . basename( dirname( $caller_file_path ) ) . '/' . basename( $caller_file_path ) ) ) ) { $module_type = WP_FS__MODULE_TYPE_THEME; /** * Relative path of the theme, e.g.: * `my-theme/functions.php` * * @author Leo Fajardo (@leorw) */ $caller_file_candidate = basename( dirname( $caller_file_path ) ) . '/' . basename( $caller_file_path ); continue; } } $caller_file_hash = md5( $caller_file_path ); if ( ! isset( $caller_map[ $caller_file_hash ] ) ) { foreach ( $all_plugins_paths as $plugin_path ) { if ( empty( $plugin_path ) ) { continue; } if ( false !== strpos( $caller_file_path, fs_normalize_path( dirname( $plugin_path ) . '/' ) ) ) { $caller_map[ $caller_file_hash ] = fs_normalize_path( $plugin_path ); break; } } } if ( isset( $caller_map[ $caller_file_hash ] ) ) { $module_type = WP_FS__MODULE_TYPE_PLUGIN; $caller_file_candidate = plugin_basename( $caller_map[ $caller_file_hash ] ); } } $caller_main_file_and_type = (object) array( 'module_type' => $module_type, 'path' => $caller_file_candidate ); return apply_filters( "fs_{$module_id}_caller_main_file_and_type", $caller_main_file_and_type ); } #---------------------------------------------------------------------------------- #region Deactivation Feedback Form #---------------------------------------------------------------------------------- /** * Displays a confirmation and feedback dialog box when the user clicks on the "Deactivate" link on the plugins * page. * * @author Vova Feldman (@svovaf) * @author Leo Fajardo (@leorw) * * @since 1.1.2 */ function _add_deactivation_feedback_dialog_box() { if ( $this->is_clone() || ( is_object( $this->_site ) && ! $this->is_registered() ) ) { return; } $subscription_cancellation_dialog_box_template_params = $this->apply_filters( 'show_deactivation_subscription_cancellation', true ) ? $this->_get_subscription_cancellation_dialog_box_template_params() : array(); /** * @since 2.3.0 Developers can optionally hide the deactivation feedback form using the 'show_deactivation_feedback_form' filter. */ $show_deactivation_feedback_form = ! self::is_deactivation_snoozed(); if ( $this->has_filter( 'show_deactivation_feedback_form' ) ) { $show_deactivation_feedback_form = $this->apply_filters( 'show_deactivation_feedback_form', true ); } else if ( $this->is_addon() ) { /** * If the add-on's 'show_deactivation_feedback_form' is not set, try to inherit the value from the parent. */ $show_deactivation_feedback_form = $this->get_parent_instance()->apply_filters( 'show_deactivation_feedback_form', true ); } $uninstall_confirmation_message = $this->apply_filters( 'uninstall_confirmation_message', '' ); if ( empty( $subscription_cancellation_dialog_box_template_params ) && ! $show_deactivation_feedback_form && empty( $uninstall_confirmation_message ) ) { return; } $vars = array( 'id' => $this->_module_id ); if ( $show_deactivation_feedback_form ) { /* Check the type of user: * 1. Long-term (long-term) * 2. Non-registered and non-anonymous short-term (non-registered-and-non-anonymous-short-term). * 3. Short-term (short-term) */ $is_long_term_user = true; // Check if the site is at least 2 days old. $time_installed = $this->_storage->install_timestamp; // Difference in seconds. $date_diff = time() - $time_installed; // Convert seconds to days. $date_diff_days = floor( $date_diff / ( 60 * 60 * 24 ) ); if ( $date_diff_days < 2 ) { $is_long_term_user = false; } $is_long_term_user = $this->apply_filters( 'is_long_term_user', $is_long_term_user ); if ( $is_long_term_user ) { $user_type = 'long-term'; } else { if ( ! $this->is_registered() && ! $this->is_anonymous() ) { $user_type = 'non-registered-and-non-anonymous-short-term'; } else { $user_type = 'short-term'; } } $uninstall_reasons = $this->_get_uninstall_reasons( $user_type ); $vars['reasons'] = $uninstall_reasons; } $vars['subscription_cancellation_dialog_box_template_params'] = &$subscription_cancellation_dialog_box_template_params; $vars['show_deactivation_feedback_form'] = $show_deactivation_feedback_form; $vars['uninstall_confirmation_message'] = $uninstall_confirmation_message; /** * Load the HTML template for the deactivation feedback dialog box. * * @todo Deactivation form core functions should be loaded only once! Otherwise, when there are multiple Freemius powered plugins the same code is loaded multiple times. The only thing that should be loaded differently is the various deactivation reasons object based on the state of the plugin. */ fs_require_template( 'forms/deactivation/form.php', $vars ); } /** * @author Leo Fajardo (@leorw) * @since 1.1.2 * * @param string $user_type * * @return array The uninstall reasons for the specified user type. */ function _get_uninstall_reasons( $user_type = 'long-term' ) { $module_type = $this->_module_type; $internal_message_template_var = array( 'id' => $this->_module_id ); $plan = $this->get_plan(); if ( $this->is_registered() && is_object( $plan ) && $plan->has_technical_support() ) { $contact_support_template = fs_get_template( 'forms/deactivation/contact.php', $internal_message_template_var ); } else { $contact_support_template = ''; } $reason_found_better_plugin = array( 'id' => self::REASON_FOUND_A_BETTER_PLUGIN, 'text' => sprintf( $this->get_text_inline( 'I found a better %s', 'reason-found-a-better-plugin' ), $module_type ), 'input_type' => 'textfield', 'input_placeholder' => sprintf( $this->get_text_inline( "What's the %s's name?", 'placeholder-plugin-name' ), $module_type ), ); $reason_temporary_deactivation = array( 'id' => self::REASON_TEMPORARY_DEACTIVATION, 'text' => sprintf( $this->get_text_inline( "It's a temporary %s - I'm troubleshooting an issue", 'reason-temporary-x' ), strtolower( $this->is_plugin() ? $this->get_text_inline( 'Deactivation', 'deactivation' ) : $this->get_text_inline( 'Theme Switch', 'theme-switch' ) ) ), 'input_type' => '', 'input_placeholder' => '' ); $reason_other = array( 'id' => self::REASON_OTHER, 'text' => $this->get_text_inline( 'Other', 'reason-other' ), 'input_type' => 'textfield', 'input_placeholder' => '' ); $long_term_user_reasons = array( array( 'id' => self::REASON_NO_LONGER_NEEDED, 'text' => sprintf( $this->get_text_inline( 'I no longer need the %s', 'reason-no-longer-needed' ), $module_type ), 'input_type' => '', 'input_placeholder' => '' ), $reason_found_better_plugin, array( 'id' => self::REASON_NEEDED_FOR_A_SHORT_PERIOD, 'text' => sprintf( $this->get_text_inline( 'I only needed the %s for a short period', 'reason-needed-for-a-short-period' ), $module_type ), 'input_type' => '', 'input_placeholder' => '' ), array( 'id' => self::REASON_BROKE_MY_SITE, 'text' => sprintf( $this->get_text_inline( 'The %s broke my site', 'reason-broke-my-site' ), $module_type ), 'input_type' => '', 'input_placeholder' => '', 'internal_message' => $contact_support_template ), array( 'id' => self::REASON_SUDDENLY_STOPPED_WORKING, 'text' => sprintf( $this->get_text_inline( 'The %s suddenly stopped working', 'reason-suddenly-stopped-working' ), $module_type ), 'input_type' => '', 'input_placeholder' => '', 'internal_message' => $contact_support_template ) ); if ( $this->is_paying() ) { $long_term_user_reasons[] = array( 'id' => self::REASON_CANT_PAY_ANYMORE, 'text' => $this->get_text_inline( "I can't pay for it anymore", 'reason-cant-pay-anymore' ), 'input_type' => 'textfield', 'input_placeholder' => $this->get_text_inline( 'What price would you feel comfortable paying?', 'placeholder-comfortable-price' ) ); } $reason_dont_share_info = array( 'id' => self::REASON_DONT_LIKE_TO_SHARE_MY_INFORMATION, 'text' => $this->get_text_inline( "I don't like to share my information with you", 'reason-dont-like-to-share-my-information' ), 'input_type' => '', 'input_placeholder' => '' ); /** * If the current user has selected the "don't share data" reason in the deactivation feedback modal, inform the * user by showing additional message that he doesn't have to share data and can just choose to skip the opt-in * (the Skip button is included in the message to show). This message will only be shown if anonymous mode is * enabled and the user's account is currently not in pending activation state (similar to the way the Skip * button in the opt-in form is shown/hidden). */ if ( $this->is_enable_anonymous() && ! $this->is_pending_activation() ) { $reason_dont_share_info['internal_message'] = fs_get_template( 'forms/deactivation/retry-skip.php', $internal_message_template_var ); } $uninstall_reasons = array( 'long-term' => $long_term_user_reasons, 'non-registered-and-non-anonymous-short-term' => array( array( 'id' => self::REASON_DIDNT_WORK, 'text' => sprintf( $this->get_text_inline( "The %s didn't work", 'reason-didnt-work' ), $module_type ), 'input_type' => '', 'input_placeholder' => '' ), $reason_dont_share_info, $reason_found_better_plugin ), 'short-term' => array( array( 'id' => self::REASON_COULDNT_MAKE_IT_WORK, 'text' => $this->get_text_inline( "I couldn't understand how to make it work", 'reason-couldnt-make-it-work' ), 'input_type' => '', 'input_placeholder' => '', 'internal_message' => $contact_support_template ), $reason_found_better_plugin, array( 'id' => self::REASON_GREAT_BUT_NEED_SPECIFIC_FEATURE, 'text' => sprintf( $this->get_text_inline( "The %s is great, but I need specific feature that you don't support", 'reason-great-but-need-specific-feature' ), $module_type ), 'input_type' => 'textarea', 'input_placeholder' => $this->get_text_inline( 'What feature?', 'placeholder-feature' ) ), array( 'id' => self::REASON_NOT_WORKING, 'text' => sprintf( $this->get_text_inline( 'The %s is not working', 'reason-not-working' ), $module_type ), 'input_type' => 'textarea', 'input_placeholder' => $this->get_text_inline( "Kindly share what didn't work so we can fix it for future users...", 'placeholder-share-what-didnt-work' ) ), array( 'id' => self::REASON_NOT_WHAT_I_WAS_LOOKING_FOR, 'text' => $this->get_text_inline( "It's not what I was looking for", 'reason-not-what-i-was-looking-for' ), 'input_type' => 'textarea', 'input_placeholder' => $this->get_text_inline( "What you've been looking for?", 'placeholder-what-youve-been-looking-for' ) ), array( 'id' => self::REASON_DIDNT_WORK_AS_EXPECTED, 'text' => sprintf( $this->get_text_inline( "The %s didn't work as expected", 'reason-didnt-work-as-expected' ), $module_type ), 'input_type' => 'textarea', 'input_placeholder' => $this->get_text_inline( 'What did you expect?', 'placeholder-what-did-you-expect' ) ) ) ); // Randomize the reasons for the current user type. shuffle( $uninstall_reasons[ $user_type ] ); // Keep the following reasons as the last items in the list. $uninstall_reasons[ $user_type ][] = $reason_temporary_deactivation; $uninstall_reasons[ $user_type ][] = $reason_other; $uninstall_reasons = $this->apply_filters( 'uninstall_reasons', $uninstall_reasons ); return $uninstall_reasons[ $user_type ]; } /** * Called after the user has submitted his reason for deactivating the plugin. * * @author Leo Fajardo (@leorw) * @since 1.1.2 */ function _submit_uninstall_reason_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'submit_uninstall_reason' ); $reason_id = fs_request_get( 'reason_id' ); // Check if the given reason ID is an unsigned integer. if ( ! ctype_digit( $reason_id ) ) { exit; } $reason_info = trim( fs_request_get( 'reason_info', '' ) ); if ( ! empty( $reason_info ) ) { $reason_info = substr( $reason_info, 0, 128 ); } $reason = (object) array( 'id' => $reason_id, 'info' => $reason_info, 'is_anonymous' => fs_request_get_bool( 'is_anonymous' ) ); $this->_storage->store( 'uninstall_reason', $reason ); if ( self::REASON_TEMPORARY_DEACTIVATION == $reason->id ) { $snooze_period = fs_request_get( 'snooze_period' ); if ( is_numeric( $snooze_period ) && 0 < $snooze_period ) { self::snooze_deactivation_form( (int) $snooze_period ); } } /** * If the module type is "theme", trigger the uninstall event here (on theme deactivation) since themes do * not support uninstall hook. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ if ( $this->is_theme() ) { if ( $this->is_premium() && ! $this->has_active_valid_license() ) { FS_Plugin_Updater::instance( $this )->delete_update_data(); } $this->_uninstall_plugin_event( false ); $this->remove_sdk_reference(); } // Print '1' for successful operation. echo 1; exit; } #-------------------------------------------------------------------------------- #region Deactivation Feedback Snoozing #-------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 2.4.3 * * @param int $period * * @return bool True if the value was set, false otherwise. */ private static function snooze_deactivation_form( $period ) { return ( 0 < $period && self::reset_deactivation_snoozing( $period ) ); } /** * Check if deactivation feedback form is snoozed. * * @author Vova Feldman (@svovaf) * @since 2.4.3 * * @return bool */ static function is_deactivation_snoozed() { $is_snoozed = ( ! is_multisite() || fs_is_network_admin() ) ? get_transient( 'fs_snooze_period' ) : get_site_transient( 'fs_snooze_period' ); return ( 'true' === $is_snoozed ); } /** * Reset deactivation snoozing. When `$period` is `0` will stop deactivation snoozing by deleting the transients. Otherwise, will set the transients for the selected period. * * @param int $period Period in seconds. * * @author Vova Feldman (@svovaf) * @since 2.4.3 */ public static function reset_deactivation_snoozing( $period = 0 ) { $value = ( 0 === $period ) ? null : 'true'; if ( ! is_multisite() || fs_is_network_admin() ) { return set_transient( 'fs_snooze_period', $value, $period ); } else { return set_site_transient( 'fs_snooze_period', $value, $period ); } } /** * The deactivation snooze expiration UNIX timestamp (in sec). * * @author Vova Feldman (@svovaf) * @since 2.4.3 * * @return int */ static function deactivation_snooze_expires_at() { return ( ! is_multisite() || fs_is_network_admin() ) ? (int) get_option( '_transient_timeout_fs_snooze_period' ) : (int) get_site_option( '_site_transient_timeout_fs_snooze_period' ); } #endregion /** * @author Leo Fajardo (@leorw) * @since 2.1.4 */ function cancel_subscription_or_trial_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'cancel_subscription_or_trial' ); $result = $this->cancel_subscription_or_trial( fs_request_get( 'plugin_id', $this->get_id() ), false ); if ( $this->is_api_error( $result ) ) { $this->shoot_ajax_failure( $result->error->message ); } $this->shoot_ajax_success(); } /** * @author Leo Fajardo (@leorw) * @since 2.1.4 * * @param number $plugin_id * * @return object */ private function cancel_subscription_or_trial( $plugin_id ) { $fs = null; if ( $plugin_id == $this->get_id() ) { $fs = $this; } else if ( $this->is_addon_activated( $plugin_id ) ) { $fs = self::get_instance_by_id( $plugin_id ); } $result = null; if ( ! is_null( $fs ) ) { $result = $fs->is_paid_trial() ? $fs->_cancel_trial() : $fs->_downgrade_site(); } return $result; } /** * @author Leo Fajardo (@leorw) * @since 2.0.2 */ function _delete_theme_update_data_action() { FS_Plugin_Updater::instance( $this )->delete_update_data(); } #endregion #---------------------------------------------------------------------------------- #region Instance #---------------------------------------------------------------------------------- /** * Main singleton instance. * * @author Vova Feldman (@svovaf) * @since 1.0.0 * * @param number $module_id * @param string|bool $slug * @param bool $is_init Is initiation sequence. * * @return Freemius|false */ static function instance( $module_id, $slug = false, $is_init = false ) { if ( empty( $module_id ) ) { return false; } /** * Load the essential static data prior to initiating FS_Plugin_Manager since there's an essential MS network migration logic that needs to be executed prior to the initiation. */ self::_load_required_static(); if ( ! is_numeric( $module_id ) ) { if ( ! $is_init && true === $slug ) { $is_init = true; } $slug = $module_id; $module = FS_Plugin_Manager::instance( $slug )->get(); if ( is_object( $module ) ) { $module_id = $module->id; } } $key = 'm_' . $module_id; if ( ! isset( self::$_instances[ $key ] ) ) { self::$_instances[ $key ] = new Freemius( $module_id, $slug, $is_init ); } return self::$_instances[ $key ]; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param number $addon_id * * @return bool */ private static function has_instance( $addon_id ) { return isset( self::$_instances[ 'm_' . $addon_id ] ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @param string|number $id_or_slug * @param string $module_type * * @return number|false */ private static function get_module_id( $id_or_slug, $module_type = WP_FS__MODULE_TYPE_PLUGIN ) { if ( is_numeric( $id_or_slug ) ) { return $id_or_slug; } foreach ( self::$_instances as $instance ) { // Also check the module type since there can be a plugin and a theme with the same slug. if ( ( $module_type === $instance->get_module_type() ) && ( $id_or_slug === $instance->get_slug() ) ) { return $instance->get_id(); } } return false; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param number $id * * @return false|Freemius */ static function get_instance_by_id( $id ) { return isset ( self::$_instances[ 'm_' . $id ] ) ? self::$_instances[ 'm_' . $id ] : false; } /** * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param string $plugin_file * @param string $module_type * * @return false|Freemius */ static function get_instance_by_file( $plugin_file, $module_type = WP_FS__MODULE_TYPE_PLUGIN ) { $slug = self::find_slug_by_basename( $plugin_file ); return ( false !== $slug ) ? self::instance( self::get_module_id( $slug, $module_type ) ) : false; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return false|Freemius */ function get_parent_instance() { return self::get_instance_by_id( $this->_plugin->parent_plugin_id ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param string|number $id_or_slug * * @return false|Freemius */ function get_addon_instance( $id_or_slug ) { $addon_id = self::get_module_id( $id_or_slug ); return self::instance( $addon_id ); } /** * @return Freemius[] */ static function _get_all_instances() { return self::$_instances; } #endregion ------------------------------------------------------------------ /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool */ function is_parent_plugin_installed() { $is_active = self::has_instance( $this->_plugin->parent_plugin_id ); if ( $is_active ) { return true; } /** * Parent module might be a theme. If that's the case, the add-on's FS * instance will be loaded prior to the theme's FS instance, therefore, * we need to check if it's active with a "look ahead". * * @author Vova Feldman * @since 1.2.2.3 */ global $fs_active_plugins; if ( is_object( $fs_active_plugins ) && is_array( $fs_active_plugins->plugins ) ) { $active_theme = wp_get_theme(); foreach ( $fs_active_plugins->plugins as $sdk => $module ) { if ( WP_FS__MODULE_TYPE_THEME === $module->type ) { if ( $module->plugin_path == $active_theme->get_stylesheet() ) { // Parent module is a theme and it's currently active. return true; } } } } return false; } /** * Check if add-on parent plugin in activation mode. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return bool */ function is_parent_in_activation() { $parent_fs = $this->get_parent_instance(); if ( ! is_object( $parent_fs ) ) { return false; } return ( $parent_fs->is_activation_mode() ); } /** * Is plugin in activation mode. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param bool $and_on * * @return bool */ function is_activation_mode( $and_on = true ) { return fs_is_network_admin() ? $this->is_network_activation_mode( $and_on ) : $this->is_site_activation_mode( $and_on ); } /** * Is plugin in activation mode. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param bool $and_on * * @return bool */ function is_site_activation_mode( $and_on = true ) { return ( ( $this->is_on() || ! $and_on ) && ( ( $this->is_premium() && true === $this->_storage->require_license_activation ) || ( ( ! $this->is_registered() || ( $this->is_only_premium() && ! $this->has_features_enabled_license() ) ) && ( ! $this->is_enable_anonymous() || ( ! $this->is_anonymous() && ! $this->is_pending_activation() ) ) ) ) ); } /** * Checks if the SDK in network activation mode. * * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param bool $and_on * * @return bool */ private function is_network_activation_mode( $and_on = true ) { if ( ! $this->_is_network_active ) { // Not network activated. return false; } if ( $this->is_network_upgrade_mode() ) { // Special flag to enforce network activation mode to decide what to do with the sites that are not yet opted-in nor skipped. return true; } if ( ! $this->is_site_activation_mode( $and_on ) ) { // Whether the context is single site or the network, if the plugin is no longer in activation mode then it is not in network activation mode as well. return false; } if ( $this->is_network_delegated_connection() ) { // Super-admin delegated the connection to the site admins -> not activation mode. return false; } if ( $this->is_network_anonymous() && true !== $this->_storage->require_license_activation ) { // Super-admin skipped the connection network wide -> not activation mode. return false; } if ( $this->is_network_registered() ) { // Super-admin connected at least one site -> not activation mode. return false; } return true; } /** * Check if current page is the opt-in/pending-activation page. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @return bool */ function is_activation_page() { if ( $this->_menu->is_activation_page( $this->show_opt_in_on_themes_page() ) ) { return true; } if ( ! $this->is_activation_mode() ) { return false; } // Check if current page is matching the activation page. return $this->is_matching_url( $this->get_activation_url() ); } /** * Check if URL path's are matching and that all querystring * arguments of the $sub_url exist in the $url with the same values. * * WARNING: * 1. This method doesn't check if the sub/domain are matching. * 2. Ignore case sensitivity. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $sub_url * @param string $url If argument is not set, check if the sub_url matching the current's page URL. * * @return bool */ private function is_matching_url( $sub_url, $url = '' ) { if ( empty( $url ) ) { $url = $_SERVER['REQUEST_URI']; } $url = strtolower( $url ); $sub_url = strtolower( $sub_url ); if ( parse_url( $sub_url, PHP_URL_PATH ) !== parse_url( $url, PHP_URL_PATH ) ) { // Different path - DO NOT OVERRIDE PAGE. return false; } $url_params = fs_parse_url_params( $url ); $sub_url_params = fs_parse_url_params( $sub_url ); foreach ( $sub_url_params as $key => $val ) { if ( ! isset( $url_params[ $key ] ) || $val != $url_params[ $key ] ) { // Not matching query string - DO NOT OVERRIDE PAGE. return false; } } return true; } /** * Get the basenames of all active plugins for specific blog. Including network activated plugins. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id * * @return string[] */ private static function get_active_plugins_basenames( $blog_id = 0 ) { if ( is_multisite() && $blog_id > 0 ) { $active_basenames = get_blog_option( $blog_id, 'active_plugins' ); } else { $active_basenames = get_option( 'active_plugins' ); } if ( ! is_array( $active_basenames ) ) { $active_basenames = array(); } if ( is_multisite() ) { $network_active_basenames = get_site_option( 'active_sitewide_plugins' ); if ( is_array( $network_active_basenames ) && ! empty( $network_active_basenames ) ) { $active_basenames = array_merge( $active_basenames, array_keys( $network_active_basenames ) ); } } return $active_basenames; } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param int $blog_id * * @return array */ static function get_active_plugins_directories_map( $blog_id = 0 ) { $active_basenames = self::get_active_plugins_basenames( $blog_id ); $map = array(); foreach ( $active_basenames as $active_basename ) { $active_basename = fs_normalize_path( $active_basename ); if ( false === strpos( $active_basename, '/' ) ) { continue; } $map[ dirname( $active_basename ) ] = true; } return $map; } /** * Get collection of all active plugins. Including network activated plugins. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param int $blog_id Since 2.0.0 * * @return array[string]array */ private static function get_active_plugins( $blog_id = 0 ) { self::require_plugin_essentials(); $active_plugin = array(); $all_plugins = fs_get_plugins(); $active_plugins_basenames = self::get_active_plugins_basenames( $blog_id ); foreach ( $active_plugins_basenames as $plugin_basename ) { $active_plugin[ $plugin_basename ] = $all_plugins[ $plugin_basename ]; } return $active_plugin; } /** * Get collection of all site active plugins for a specified blog. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id * * @return array[string]array */ private static function get_site_active_plugins( $blog_id = 0 ) { $active_basenames = ( is_multisite() && $blog_id > 0 ) ? get_blog_option( $blog_id, 'active_plugins' ) : get_option( 'active_plugins' ); $active = array(); if ( ! is_array( $active_basenames ) ) { return $active; } foreach ( $active_basenames as $basename ) { $active[ $basename ] = array( 'is_active' => true, 'Version' => '1.0', // Dummy version. 'slug' => self::get_plugin_slug( $basename ), ); } return $active; } /** * Get collection of all plugins with their activation status for a specified blog. * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @param int $blog_id Since 2.0.0 * * @return array Key is the plugin file path and the value is an array of the plugin data. */ private static function get_all_plugins( $blog_id = 0 ) { self::require_plugin_essentials(); $all_plugins = fs_get_plugins(); $active_plugins_basenames = self::get_active_plugins_basenames( $blog_id ); foreach ( $all_plugins as $basename => &$data ) { // By default set to inactive (next foreach update the active plugins). $data['is_active'] = false; // Enrich with plugin slug. $data['slug'] = self::get_plugin_slug( $basename ); } // Flag active plugins. foreach ( $active_plugins_basenames as $basename ) { if ( isset( $all_plugins[ $basename ] ) ) { $all_plugins[ $basename ]['is_active'] = true; } } return $all_plugins; } /** * Get collection of all plugins and if they are network level activated. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return array Key is the plugin basename and the value is an array of the plugin data. */ private static function get_network_plugins() { self::require_plugin_essentials(); $all_plugins = fs_get_plugins(); $network_active_basenames = is_multisite() ? get_site_option( 'active_sitewide_plugins' ) : array(); foreach ( $all_plugins as $basename => &$data ) { // By default set to inactive (next foreach update the active plugins). $data['is_active'] = false; // Enrich with plugin slug. $data['slug'] = self::get_plugin_slug( $basename ); } // Flag active plugins. foreach ( $network_active_basenames as $basename ) { if ( isset( $all_plugins[ $basename ] ) ) { $all_plugins[ $basename ]['is_active'] = true; } } return $all_plugins; } /** * Cached result of get_site_transient( 'update_plugins' ) * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @var object */ private static $_plugins_info; /** * Helper function to get specified plugin's slug. * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @param $basename * * @return string */ private static function get_plugin_slug( $basename ) { if ( ! isset( self::$_plugins_info ) ) { self::$_plugins_info = get_site_transient( 'update_plugins' ); } $slug = ''; if ( is_object( self::$_plugins_info ) ) { if ( isset( self::$_plugins_info->no_update ) && isset( self::$_plugins_info->no_update[ $basename ] ) && ! empty( self::$_plugins_info->no_update[ $basename ]->slug ) ) { $slug = self::$_plugins_info->no_update[ $basename ]->slug; } else if ( isset( self::$_plugins_info->response ) && isset( self::$_plugins_info->response[ $basename ] ) && ! empty( self::$_plugins_info->response[ $basename ]->slug ) ) { $slug = self::$_plugins_info->response[ $basename ]->slug; } } if ( empty( $slug ) ) { // Try to find slug from FS data. $slug = self::find_slug_by_basename( $basename ); } if ( empty( $slug ) ) { // Fallback to plugin's folder name. $slug = dirname( $basename ); } return $slug; } private static $_statics_loaded = false; /** * Load static resources. * * @author Vova Feldman (@svovaf) * @since 1.0.1 */ private static function _load_required_static() { if ( self::$_statics_loaded ) { return; } self::$_static_logger = FS_Logger::get_logger( WP_FS__SLUG, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK ); self::$_static_logger->entrance(); self::$_accounts = FS_Options::instance( WP_FS__ACCOUNTS_OPTION_NAME, true ); if ( is_multisite() ) { $has_skipped_migration = ( // 'id_slug_type_path_map' - was never stored on older versions, therefore, not exists on the site level. null === self::$_accounts->get_option( 'id_slug_type_path_map', null, false ) && // 'file_slug_map' stored on the site level, so it was running an SDK version before it was integrated with MS-network. null !== self::$_accounts->get_option( 'file_slug_map', null, false ) ); /** * If the file_slug_map exists on the site level but doesn't exist on the * network level storage, it means that we need to process the storage with migration. * * The code in this `if` scope will only be executed once and only for the first site that will execute it because once we migrate the storage data, file_slug_map will be already set in the network level storage. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ if ( ( $has_skipped_migration && true !== self::$_accounts->get_option( 'ms_migration_complete', false, true ) ) || ( null === self::$_accounts->get_option( 'file_slug_map', null, true ) && null !== self::$_accounts->get_option( 'file_slug_map', null, false ) ) ) { self::migrate_options_to_network(); } } self::$_global_admin_notices = FS_Admin_Notices::instance( 'global' ); FS_DebugManager::load_required_static(); if ( 0 == did_action( 'plugins_loaded' ) ) { add_action( 'plugins_loaded', array( 'Freemius', '_load_textdomain' ), 1 ); } $clone_manager = FS_Clone_Manager::instance(); add_action( 'init', array( $clone_manager, '_init' ) ); add_action( 'admin_footer', array( 'Freemius', '_handle_submenu_external_link' ) ); if ( self::is_plugins_page() || self::is_themes_page() ) { add_action( 'admin_print_footer_scripts', array( 'Freemius', '_maybe_add_beta_label_styles' ), 9 ); /** * Specifically use this hook so that the JS event handlers will work properly on the "Themes" * page. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ add_action( 'admin_footer-' . self::get_current_page(), array( 'Freemius', '_maybe_add_beta_label_to_plugins_and_handle_confirmation') ); } self::$_statics_loaded = true; } public static function get_static_logger() { return self::$_static_logger; } public static function get_accounts() { return self::$_accounts; } #-------------------------------------------------------------------------------- #region Clone #-------------------------------------------------------------------------------- /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param bool $only_if_manual_resolution_is_not_hidden * * @return bool */ private function is_unresolved_clone( $only_if_manual_resolution_is_not_hidden = false ) { if ( ! $this->is_clone( $only_if_manual_resolution_is_not_hidden ) ) { return false; } return FS_Clone_Manager::instance()->has_temporary_duplicate_mode_expired(); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param bool $only_if_manual_resolution_is_not_hidden */ function is_clone( $only_if_manual_resolution_is_not_hidden = false ) { if ( ! is_object( $this->_site ) ) { return false; } $blog_id = null; if ( fs_is_network_admin() && FS_Site::is_valid_id( $this->_storage->network_install_blog_id ) ) { // Ensure that we're comparing the network install's URL with the relevant subsite's URL. $blog_id = $this->_storage->network_install_blog_id; } $site_url = Freemius::get_unfiltered_site_url( $blog_id, true, true ); if ( ! $this->_site->is_clone( $site_url ) ) { return false; } return ( ! $only_if_manual_resolution_is_not_hidden || ! FS_Clone_Manager::instance()->should_hide_manual_resolution() ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param int|null $blog_id * @param bool $strip_protocol * @param bool $add_trailing_slash * * @return string */ static function get_unfiltered_site_url( $blog_id = null, $strip_protocol = false, $add_trailing_slash = false ) { $url = ( ! is_multisite() && defined( 'WP_SITEURL' ) ) ? WP_SITEURL : self::get_site_url_from_wp_option( $blog_id ); if ( $strip_protocol ) { $url = fs_strip_url_protocol( $url ); } if ( $add_trailing_slash ) { $url = trailingslashit( $url ); } return $url; } /** * @author Leo Fajardo (@leorw) * @since 2.6.0 * * @param int|null $blog_id * * @return string */ private static function get_site_url_from_wp_option( $blog_id = null ) { global $wp_filter; $site_url_filters = array( 'site_url' => null, 'pre_option_siteurl' => null, 'default_option_siteurl' => null, 'option_siteurl' => null, ); // Detach all URL-related filters to get the actual site's URL (stripped of potential manipulations by multilingual plugins). foreach ( $site_url_filters as $hook_name => $site_url_filter ) { if ( ! empty( $wp_filter[ $hook_name ] ) ) { $site_url_filters[ $hook_name ] = $wp_filter[ $hook_name ]; unset( $wp_filter[ $hook_name ] ); } } $url = get_site_url( $blog_id ); // Re-attach the filters back. foreach ( $site_url_filters as $hook_name => $site_url_filter ) { if ( ! empty( $site_url_filter ) ) { $wp_filter[ $hook_name ] = $site_url_filter; } } return $url; } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param number $site_id */ function fetch_install_by_id( $site_id ) { return $this->get_current_or_network_user_api_scope()->get( "/installs/{$site_id}.json" ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @return string|object|bool */ function _handle_long_term_duplicate() { $this->_logger->entrance(); $this->delete_current_install( false ); $license_key = false; if ( is_object( $this->_license ) && ! $this->_license->is_utilized( ( WP_FS__IS_LOCALHOST_FOR_SERVER || FS_Site::is_localhost_by_address( self::get_unfiltered_site_url() ) ) ) ) { $license_key = $this->_license->secret_key; } return $this->opt_in( false, false, false, $license_key, false, false, false, null, array(), false ); } #endregion /** * @author Leo Fajardo (@leorw) * * @since 2.1.3 */ public static function migrate_options_to_network() { self::migrate_accounts_to_network(); // Migrate API options from site level to network level. $api_network_options = FS_Option_Manager::get_manager( WP_FS__OPTIONS_OPTION_NAME, true, true ); $api_network_options->migrate_to_network(); // Migrate API cache to network level storage. FS_Cache_Manager::get_manager( WP_FS__API_CACHE_OPTION_NAME )->migrate_to_network(); self::$_accounts->set_option( 'ms_migration_complete', true, true ); } #---------------------------------------------------------------------------------- #region Localization #---------------------------------------------------------------------------------- /** * Load framework's text domain. * * @author Vova Feldman (@svovaf) * @since 1.2.1 */ static function _load_textdomain() { if ( ! is_admin() ) { return; } global $fs_active_plugins; // Works both for plugins and themes. load_plugin_textdomain( 'freemius', false, $fs_active_plugins->newest->sdk_path . '/languages/' ); } #endregion #---------------------------------------------------------------------------------- #region Connectivity Issues #---------------------------------------------------------------------------------- /** * Check if Freemius should be turned on for the current plugin install. * * Note: * $this->_is_on is updated in has_api_connectivity() * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_on() { self::$_static_logger->entrance(); if ( is_object( $this->_site ) && ! $this->is_registered() ) { return false; } if ( isset( $this->_is_on ) ) { return $this->_is_on; } // If already installed or pending then sure it's on :) if ( $this->is_registered() || $this->is_pending_activation() ) { $this->_is_on = true; return true; } return false; } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @param bool $flush_if_no_connectivity * * @return bool */ private function should_run_connectivity_test( $flush_if_no_connectivity = false ) { if ( ! isset( $this->_storage->connectivity_test ) ) { // Connectivity test was never executed, or cache was cleared. return true; } if ( WP_FS__PING_API_ON_IP_OR_HOST_CHANGES ) { if ( WP_FS__IS_HTTP_REQUEST ) { if ( $_SERVER['HTTP_HOST'] != $this->_storage->connectivity_test['host'] ) { // Domain changed. return true; } if ( WP_FS__REMOTE_ADDR != $this->_storage->connectivity_test['server_ip'] ) { // Server IP changed. return true; } } } if ( $this->_storage->connectivity_test['is_connected'] && $this->_storage->connectivity_test['is_active'] ) { // API connected and Freemius is active - no need to run connectivity check. return false; } if ( $flush_if_no_connectivity ) { /** * If explicitly asked to flush when no connectivity - do it only * if at least 10 sec passed from the last API connectivity test. */ return ( isset( $this->_storage->connectivity_test['timestamp'] ) && ( WP_FS__SCRIPT_START_TIME - $this->_storage->connectivity_test['timestamp'] ) > 10 ); } /** * @since 1.1.7 Don't check for connectivity on plugin downgrade. */ $version = $this->get_plugin_version(); if ( version_compare( $version, $this->_storage->connectivity_test['version'], '>' ) ) { // If it's a plugin version upgrade and Freemius is off or no connectivity, run connectivity test. return true; } return false; } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 * * @param bool $is_update * * @return bool */ private function should_turn_fs_on( $is_update = true ) { if ( empty( $this->_plugin->opt_in_moderation ) || ! is_array( $this->_plugin->opt_in_moderation ) ) { return true; } $optin_config = $this->_plugin->opt_in_moderation; if ( WP_FS__IS_LOCALHOST && ( ! isset( $optin_config['localhost'] ) || false !== $optin_config['localhost'] ) ) { return true; } $optin_config_key = $is_update ? 'updates' : 'new'; if ( ! isset( $optin_config[ $optin_config_key ] ) ) { return true; } $visibility_percentage = $optin_config[ $optin_config_key ]; if ( 0 == $visibility_percentage ) { return false; } if ( ! is_numeric( $visibility_percentage ) ) { return true; } $min = 1; $max = 100; if ( function_exists( 'random_int' ) ) { $random = random_int( $min, $max ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.random_intFound } else { $random = rand( $min, $max ); } return ( $random <= $visibility_percentage ); } /** * Check if there's any connectivity issue to Freemius API. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param bool $flush_if_no_connectivity * * @return bool|null */ function has_api_connectivity( $flush_if_no_connectivity = false ) { $this->_logger->entrance(); if ( isset( $this->_has_api_connection ) && ( $this->_has_api_connection || ! $flush_if_no_connectivity ) ) { return $this->_has_api_connection; } if ( WP_FS__SIMULATE_NO_API_CONNECTIVITY && isset( $this->_storage->connectivity_test ) && true === $this->_storage->connectivity_test['is_connected'] ) { $this->clear_connectivity_info(); } if ( ! $this->should_run_connectivity_test( $flush_if_no_connectivity ) ) { $this->_has_api_connection = $this->_storage->connectivity_test['is_connected']; /** * @since 1.1.6 During dev mode, if there's connectivity - turn Freemius on regardless the configuration. * * @since 1.2.1.5 If the user running the premium version then ignore the 'is_active' flag and turn Freemius on to enable license key activation. */ $this->_is_on = $this->_storage->connectivity_test['is_active'] || $this->is_premium() || ( WP_FS__DEV_MODE && $this->_has_api_connection && ! WP_FS__SIMULATE_FREEMIUS_OFF ); return $this->_has_api_connection; } if ( ! empty( $this->_storage->connectivity_test ) && isset( $this->_storage->connectivity_test['is_active'] ) ) { $is_connected = isset( $this->_storage->connectivity_test['is_connected'] ) ? $this->_storage->connectivity_test['is_connected'] : null; $is_active = ( $this->_storage->connectivity_test['is_active'] || is_object( $this->_site ) ); } else { $is_connected = null; $is_active = $this->should_turn_fs_on( $this->apply_filters( 'is_plugin_update', $this->is_plugin_update() ) ); } $this->store_connectivity_info( (object) array( 'is_active' => $is_active ), $is_connected ); if ( $is_active ) { $this->_is_on = true; } return $this->_has_api_connection; } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 */ private function clear_connectivity_info() { unset( $this->_storage->connectivity_test ); FS_Api::clear_force_http_flag(); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.4 * * @param object $pong * @param bool|null $is_connected */ private function store_connectivity_info( $pong, $is_connected ) { $this->_logger->entrance(); $version = $this->get_plugin_version(); if ( false === $is_connected || WP_FS__SIMULATE_FREEMIUS_OFF ) { $is_active = false; } else { $is_active = ( isset( $pong->is_active ) && true == $pong->is_active ); } $is_active = $this->apply_filters( 'is_on', $is_active, $this->is_plugin_update(), $version ); $this->_storage->connectivity_test = array( 'is_connected' => $is_connected, 'host' => $_SERVER['HTTP_HOST'], 'server_ip' => WP_FS__REMOTE_ADDR, 'is_active' => $is_active, 'timestamp' => WP_FS__SCRIPT_START_TIME, // Last version with connectivity attempt. 'version' => $version, ); $this->_has_api_connection = $is_connected; $this->_is_on = $is_active || ( WP_FS__DEV_MODE && $is_connected && ! WP_FS__SIMULATE_FREEMIUS_OFF ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 * * @param bool $is_connected */ private function update_connectivity_info( $is_connected ) { $this->store_connectivity_info( // This is true since we update the connection info only after a successful opt-in or license activation which means that Freemius has already been on even before the process. (object) array( 'is_active' => true ), $is_connected ); } /** * Force turning Freemius on. * * @author Vova Feldman (@svovaf) * @since 1.1.8.1 * * @return bool TRUE if successfully turned on. */ private function turn_on() { $this->_logger->entrance(); if ( $this->is_on() || ! isset( $this->_storage->connectivity_test['is_active'] ) ) { return false; } $updated_connectivity = $this->_storage->connectivity_test; $updated_connectivity['is_active'] = true; $updated_connectivity['timestamp'] = WP_FS__SCRIPT_START_TIME; $this->_storage->connectivity_test = $updated_connectivity; $this->_is_on = true; return true; } /** * Anonymous and unique site identifier (Hash). * * @author Vova Feldman (@svovaf) * @since 1.1.0 * * @param null|int $blog_id Since 2.0.0 * * @return string */ function get_anonymous_id( $blog_id = null ) { $unique_id = self::$_accounts->get_option( 'unique_id', null, $blog_id ); if ( empty( $unique_id ) || ! is_string( $unique_id ) ) { $key = self::get_unfiltered_site_url( $blog_id, true ); $secure_auth = defined( 'SECURE_AUTH_KEY' ) ? SECURE_AUTH_KEY : ''; if ( empty( $secure_auth ) || false !== strpos( $secure_auth, ' ' ) || 'put your unique phrase here' === $secure_auth ) { // Protect against default auth key. $secure_auth = md5( microtime() ); } /** * Base the unique identifier on the WP secure authentication key. Which * turns the key into a secret anonymous identifier. This will help us * to avoid duplicate installs generation on the backend upon opt-in. * * @author Vova Feldman (@svovaf) * @since 1.2.3 */ $unique_id = md5( $key . $secure_auth ); self::$_accounts->set_option( 'unique_id', $unique_id, true, $blog_id ); } $this->_logger->departure( $unique_id ); return $unique_id; } /** * Returns anonymous network ID. * * @since 2.4.3 * * @return string */ function get_anonymous_network_id() { return $this->get_anonymous_id( get_network()->site_id ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.4 * * @return \WP_User */ static function _get_current_wp_user() { self::require_pluggable_essentials(); self::wp_cookie_constants(); return wp_get_current_user(); } /** * Define cookie constants which are required by Freemius::_get_current_wp_user() since * it uses wp_get_current_user() which needs the cookie constants set. When a plugin * is network activated the cookie constants are only configured after the network * plugins activation, therefore, if we don't define those constants WP will throw * PHP warnings/notices. * * @author Vova Feldman (@svovaf) * @since 2.1.1 */ private static function wp_cookie_constants() { if ( defined( 'LOGGED_IN_COOKIE' ) && ( defined( 'AUTH_COOKIE' ) || defined( 'SECURE_AUTH_COOKIE' ) ) ) { return; } /** * Used to guarantee unique hash cookies * * @since 1.5.0 */ if ( ! defined( 'COOKIEHASH' ) ) { $siteurl = get_site_option( 'siteurl' ); if ( $siteurl ) { define( 'COOKIEHASH', md5( $siteurl ) ); } else { define( 'COOKIEHASH', '' ); } } if ( ! defined( 'LOGGED_IN_COOKIE' ) ) { define( 'LOGGED_IN_COOKIE', 'wordpress_logged_in_' . COOKIEHASH ); } /** * @since 2.5.0 */ if ( ! defined( 'AUTH_COOKIE' ) ) { define( 'AUTH_COOKIE', 'wordpress_' . COOKIEHASH ); } /** * @since 2.6.0 */ if ( ! defined( 'SECURE_AUTH_COOKIE' ) ) { define( 'SECURE_AUTH_COOKIE', 'wordpress_sec_' . COOKIEHASH ); } } /** * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @return int */ static function get_current_wp_user_id() { $wp_user = self::_get_current_wp_user(); return $wp_user->ID; } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $email * * @return bool */ static function is_valid_email( $email ) { if ( false === filter_var( $email, FILTER_VALIDATE_EMAIL ) ) { return false; } $parts = explode( '@', $email ); if ( 2 !== count( $parts ) || empty( $parts[1] ) ) { return false; } $blacklist = array( 'admin.', 'webmaster.', 'localhost.', 'dev.', 'development.', 'test.', 'stage.', 'staging.', ); // Make sure domain is not one of the blacklisted. foreach ( $blacklist as $invalid ) { if ( 0 === strpos( $parts[1], $invalid ) ) { return false; } } // Get the UTF encoded domain name. /** * @note - The check of `defined('...')` is there to account for PHP servers compiled with some older version of ICU where the constants are not defined. * @author - @swashata */ $is_new_idn_available = ( version_compare( PHP_VERSION, '5.6.40') > 0 && defined( 'IDNA_DEFAULT' ) && defined( 'INTL_IDNA_VARIANT_UTS46' ) ); if ( $is_new_idn_available ) { $domain = idn_to_ascii( $parts[1], IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46 ); } else { $domain = idn_to_ascii( $parts[1] ); // phpcs:ignore PHPCompatibility.ParameterValues.NewIDNVariantDefault.NotSet } $domain = $domain . '.'; return ( checkdnsrr( $domain, 'MX' ) || checkdnsrr( $domain, 'A' ) ); } #endregion #---------------------------------------------------------------------------------- #region Email #---------------------------------------------------------------------------------- /** * Generates and sends an HTML email with customizable sections. * * @author Leo Fajardo (@leorw) * @since 1.1.2 * * @param string $to_address * @param string $subject * @param array $sections * @param array $headers * * @return bool Whether the email contents were sent successfully. */ private function send_email( $to_address, $subject, $sections = array(), $headers = array() ) { $default_sections = $this->get_email_sections(); // Insert new sections or replace the default email sections. if ( is_array( $sections ) && ! empty( $sections ) ) { foreach ( $sections as $section_id => $custom_section ) { if ( ! isset( $default_sections[ $section_id ] ) ) { // If the section does not exist, add it. $default_sections[ $section_id ] = $custom_section; } else { // If the section already exists, override it. $current_section = $default_sections[ $section_id ]; // Replace the current section's title if a custom section title exists. if ( isset( $custom_section['title'] ) ) { $current_section['title'] = $custom_section['title']; } // Insert new rows under the current section or replace the default rows. if ( isset( $custom_section['rows'] ) && is_array( $custom_section['rows'] ) && ! empty( $custom_section['rows'] ) ) { foreach ( $custom_section['rows'] as $row_id => $row ) { $current_section['rows'][ $row_id ] = $row; } } $default_sections[ $section_id ] = $current_section; } } } $vars = array( 'sections' => $default_sections ); $message = fs_get_template( 'email.php', $vars ); // Set the type of email to HTML. $headers[] = 'Content-type: text/html; charset=UTF-8'; $header_string = implode( "\r\n", $headers ); return wp_mail( $to_address, $subject, $message, $header_string ); } /** * Generates the data for the sections of the email content. * * @author Leo Fajardo (@leorw) * @since 1.1.2 * * @return array */ private function get_email_sections() { // Retrieve the current user's information so that we can get the user's email, first name, and last name below. $current_user = self::_get_current_wp_user(); // Retrieve the cURL version information so that we can get the version number below. $curl_version_information = curl_version(); $active_plugin = self::get_active_plugins(); // Generate the list of active plugins separated by new line. $active_plugin_string = ''; foreach ( $active_plugin as $plugin ) { $active_plugin_string .= sprintf( '%s [v%s]
    ', $plugin['PluginURI'], $plugin['Name'], $plugin['Version'] ); } $server_ip = WP_FS__REMOTE_ADDR; // Add PHP info for deeper investigation. ob_start(); phpinfo(); $php_info = ob_get_clean(); $api_domain = substr( FS_API__ADDRESS, strpos( FS_API__ADDRESS, ':' ) + 3 ); // Generate the default email sections. $sections = array( 'sdk' => array( 'title' => 'SDK', 'rows' => array( 'fs_version' => array( 'FS Version', $this->version ), 'curl_version' => array( 'cURL Version', $curl_version_information['version'] ) ) ), 'plugin' => array( 'title' => ucfirst( $this->get_module_type() ), 'rows' => array( 'name' => array( 'Name', $this->get_plugin_name() ), 'version' => array( 'Version', $this->get_plugin_version() ) ) ), 'api' => array( 'title' => 'API Subdomain', 'rows' => array( 'dns' => array( 'DNS_CNAME', function_exists( 'dns_get_record' ) ? var_export( dns_get_record( $api_domain, DNS_CNAME ), true ) : 'dns_get_record() disabled/blocked' ), 'ip' => array( 'IP', function_exists( 'gethostbyname' ) ? gethostbyname( $api_domain ) : 'gethostbyname() disabled/blocked' ), ), ), 'site' => array( 'title' => 'Site', 'rows' => array( 'unique_id' => array( 'Unique ID', $this->get_anonymous_id() ), 'address' => array( 'Address', site_url() ), 'host' => array( 'HTTP_HOST', ( ! empty( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '' ) ), 'hosting' => array( 'Hosting Company' => fs_request_has( 'hosting_company' ) ? fs_request_get( 'hosting_company' ) : 'Unknown', ), 'server_addr' => array( 'SERVER_ADDR', '' . $server_ip . '' ) ) ), 'user' => array( 'title' => 'User', 'rows' => array( 'email' => array( 'Email', $current_user->user_email ), 'first' => array( 'First', $current_user->user_firstname ), 'last' => array( 'Last', $current_user->user_lastname ) ) ), 'plugins' => array( 'title' => 'Plugins', 'rows' => array( 'active_plugins' => array( 'Active Plugins', $active_plugin_string ) ) ), 'php_info' => array( 'title' => 'PHP Info', 'rows' => array( 'info' => array( $php_info ) ), ) ); // Allow the sections to be modified by other code. $sections = $this->apply_filters( 'email_template_sections', $sections ); return $sections; } #endregion #---------------------------------------------------------------------------------- #region Initialization #---------------------------------------------------------------------------------- /** * Init plugin's Freemius instance. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param number $id * @param string $public_key * @param bool $is_live * @param bool $is_premium */ function init( $id, $public_key, $is_live = true, $is_premium = true ) { $this->_logger->entrance(); $this->dynamic_init( array( 'id' => $id, 'public_key' => $public_key, 'is_live' => $is_live, 'is_premium' => $is_premium, ) ); } /** * Dynamic initiator, originally created to support initiation * with parent_id for add-ons. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param array $plugin_info * * @throws Freemius_Exception */ function dynamic_init( array $plugin_info ) { $this->_logger->entrance(); $this->parse_settings( $plugin_info ); $this->register_after_settings_parse_hooks(); /** * If anonymous but there's already a user entity and the user's site is associated with a valid license or trial period, update the anonymous mode accordingly. * * @todo Remove this entire `if` block after several releases as starting from this version, the anonymous mode will already be updated accordingly after a purchase. */ if ( $this->is_anonymous() ) { $is_network_level = ( $this->_is_network_active && fs_is_network_admin() ); if ( ! $is_network_level || FS_Site::is_valid_id( $this->_storage->network_install_blog_id ) ) { if ( $this->is_paying_or_trial() ) { $this->reset_anonymous_mode( $is_network_level ); } } else { $network = get_network(); if ( is_object( $network ) ) { $main_blog_id = $network->site_id; $first_install = $this->get_install_by_blog_id( $main_blog_id ); if ( is_object( $first_install ) ) { $this->_storage->network_install_blog_id = $main_blog_id; $this->_storage->network_user_id = $first_install->user_id; } } } } if ( $this->should_stop_execution() ) { return; } if ( ! $this->is_registered() ) { if ( $this->is_anonymous() ) { // If user skipped, no need to test connectivity. $this->_has_api_connection = true; $this->_is_on = true; } else { if ( false === $this->has_api_connectivity() ) { return; } else { if ( $this->_anonymous_mode ) { // Simulate anonymous mode. $this->_is_anonymous = true; } } } } /** * This should be executed even if Freemius is off for the core module, * otherwise, the add-ons dialog box won't work properly. This is especially * relevant when the developer decided to turn FS off for existing users. * * @author Vova Feldman (@svovaf) */ if ( $this->is_user_in_admin() && 'plugin-information' === fs_request_get( 'tab', false ) && $this->should_use_freemius_updater_and_dialog() && ( ( $this->is_addon() && $this->get_slug() == fs_request_get( 'plugin', false ) ) || ( $this->has_addons() && $this->get_id() == fs_request_get( 'parent_plugin_id', false ) ) ) ) { require_once WP_FS__DIR_INCLUDES . '/fs-plugin-info-dialog.php'; new FS_Plugin_Info_Dialog( $this->is_addon() ? $this->get_parent_instance() : $this ); } // Check if Freemius is on for the current plugin. // This MUST be executed after all the plugin variables has been loaded. if ( ! $this->is_registered() && ! $this->is_on() ) { return; } if ( $this->has_api_connectivity() ) { if ( self::is_cron() ) { $this->hook_callback_to_sync_cron(); } else if ( $this->is_user_in_admin() ) { /** * Schedule daily data sync cron if: * * 1. User opted-in (for tracking). * 2. If skipped, but later upgraded (opted-in via upgrade). * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * */ if ( $this->is_registered() && $this->is_tracking_allowed() ) { $this->maybe_schedule_sync_cron(); } /** * Check if requested for manual blocking background sync. */ if ( fs_request_has( 'background_sync' ) ) { self::require_pluggable_essentials(); self::wp_cookie_constants(); $this->run_manual_sync(); } } } if ( $this->is_registered() ) { FS_Clone_Manager::instance()->maybe_resolve_new_subsite_install_automatically( $this ); $this->hook_callback_to_install_sync(); } if ( $this->is_addon() ) { if ( $this->is_parent_plugin_installed() ) { // Link to parent FS. $this->_parent = self::get_instance_by_id( $this->_plugin->parent_plugin_id ); // Get parent plugin reference. $this->_parent_plugin = $this->_parent->get_plugin(); } } if ( $this->is_user_in_admin() ) { if ( $this->is_registered() && fs_request_has( 'purchase_completed' ) ) { $this->_admin_notices->add_sticky( sprintf( /* translators: %s: License type (e.g. you have a professional license) */ $this->get_text_inline( 'You have purchased a %s license.', 'you-have-x-license' ), fs_request_get( 'purchased_plan' ) ) . sprintf( $this->get_text_inline(" The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box.", 'post-purchase-email-sent-message' ), $this->get_module_label( true ), ( FS_Plugin::is_valid_id( $this->get_bundle_id() ) ? "products' " : '' ), ( FS_Plugin::is_valid_id( $this->get_bundle_id() ) ? 's' : '' ), sprintf( '%s', fs_request_get( 'purchase_email' ) ) ), 'plan_purchased', $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!' ); } if ( $this->is_addon() ) { if ( ! $this->is_parent_plugin_installed() ) { $parent_name = $this->get_option( $plugin_info, 'parent_name', null ); if ( isset( $plugin_info['parent'] ) ) { $parent_name = $this->get_option( $plugin_info['parent'], 'name', null ); } $this->_admin_notices->add( ( ! empty( $parent_name ) ? sprintf( $this->get_text_x_inline( '%s cannot run without %s.', 'addonX cannot run without pluginY', 'addon-x-cannot-run-without-y' ), $this->get_plugin_name(), $parent_name ) : sprintf( $this->get_text_x_inline( '%s cannot run without the plugin.', 'addonX cannot run...', 'addon-x-cannot-run-without-parent' ), $this->get_plugin_name() ) ), $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 'error' ); return; } else { $is_network_admin = fs_is_network_admin(); if ( ! $this->_parent->is_registered() && $this->is_registered() ) { // If add-on activated and parent not, automatically install parent for the user. $this->activate_parent_account( $this->_parent ); } else if ( $this->_parent->is_registered() && ! $this->is_registered() && /** * If not registered for add-on and the following conditions for the add-on are met, activate add-on account. * * Network active and in network admin - network activate add-on account. * * Network active and not in network admin - activate add-on account for the current blog. * * Not network active and not in network admin - activate add-on account for the current blog. * * If not registered for add-on, not network active, and in network admin, do not handle the add-on activation. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ ( $this->is_network_active() || ! $is_network_admin ) ) { $premium_license = null; if ( ! $this->has_free_plan() && $this->is_bundle_license_auto_activation_enabled() && $this->_parent->is_activated_with_bundle_license() ) { /** * If the add-on has no free plan, try to activate the account only when there's a bundle license. * * @author Leo Fajardo (@leorw) * @since 2.4.0 */ $bundle_license = $this->get_active_parent_license( $this->_parent->_get_license()->secret_key, false ); if ( is_object( $bundle_license ) && ! empty( $bundle_license->products ) && in_array( $this->get_id(), $bundle_license->products ) ) { $premium_license = $bundle_license; } } if ( $this->has_free_plan() || is_object( $premium_license) ) { // If parent plugin activated, automatically install add-on for the user. $this->_activate_addon_account( $this->_parent, ( $this->is_network_active() && $is_network_admin ) ? true : get_current_blog_id(), $premium_license ); } } // @todo This should be only executed on activation. It should be migrated to register_activation_hook() together with other activation related logic. if ( $this->is_premium() ) { // Remove add-on download admin-notice. $this->_parent->_admin_notices->remove_sticky( array( 'addon_plan_upgraded_' . $this->_slug, 'no_addon_license_' . $this->_slug, ) ); } // $this->deactivate_premium_only_addon_without_license(); } } add_action( 'admin_init', array( &$this, '_admin_init_action' ) ); // if ( $this->is_registered() || // $this->is_anonymous() || // $this->is_pending_activation() // ) { // $this->_init_admin(); // } } /** * Should be called outside `$this->is_user_in_admin()` scope * because the updater has some logic that needs to be executed * during AJAX calls. * * Currently, we need to hook to the `http_request_host_is_external` filter. * In the future, there might be additional logic added. * * @author Vova Feldman * @since 1.2.1.6 */ if ( $this->should_use_freemius_updater_and_dialog() && ( $this->is_premium() || /** * If not premium but the premium version is installed, also instantiate the updater so that the * plugin information dialog of the premium version will have the information from the server. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $this->premium_plugin_basename() ) ) ) ) && $this->has_release_on_freemius() && ( ! $this->is_unresolved_clone( true ) ) ) { FS_Plugin_Updater::instance( $this ); } $this->do_action( 'initiated' ); if ( $this->_storage->prev_is_premium !== $this->_plugin->is_premium ) { if ( isset( $this->_storage->prev_is_premium ) ) { $this->apply_filters( 'after_code_type_change', // New code type. $this->_plugin->is_premium ); } else { // Set for code type for the first time. $this->_storage->prev_is_premium = $this->_plugin->is_premium; } } if ( ! $this->is_addon() ) { if ( $this->is_registered() ) { // Fix for upgrade from versions < 1.0.9. if ( ! isset( $this->_storage->activation_timestamp ) ) { $this->_storage->activation_timestamp = WP_FS__SCRIPT_START_TIME; } $this->do_action( 'after_init_plugin_registered' ); } else if ( $this->is_anonymous() ) { $this->do_action( 'after_init_plugin_anonymous' ); } else if ( $this->is_pending_activation() ) { $this->do_action( 'after_init_plugin_pending_activations' ); } } else { if ( $this->is_registered() ) { $this->do_action( 'after_init_addon_registered' ); } else if ( $this->is_anonymous() ) { $this->do_action( 'after_init_addon_anonymous' ); } else if ( $this->is_pending_activation() ) { $this->do_action( 'after_init_addon_pending_activations' ); } } } /** * @author Leo Fajardo (@leorw) * @since 2.2.3 * * @return bool */ private function should_use_freemius_updater_and_dialog() { return ( /** * Allow updater and dialog when the `fs_allow_updater_and_dialog` URL query param exists and has `true` * value, or when the current page is not the "Add Plugins" page (/plugin-install.php) and the `action` * URL query param doesn't exist or its value is not `install-plugin` so that there will be no conflicts * with the .org plugins' functionalities (e.g. installation from the "Add Plugins" page and viewing * plugin details from .org). */ ( true === fs_request_get_bool( 'fs_allow_updater_and_dialog' ) ) || ( ! self::is_plugin_install_page() && // Disallow updater and dialog when installing a plugin, otherwise .org "add-on" plugins will be affected. ( 'install-plugin' !== fs_request_get( 'action' ) ) ) ); } /** * @param string[] $permissions * @param bool $is_enabled * @param int|null $blog_id * * @return true|object `true` on success, API error object on failure. */ private function update_site_permissions( array $permissions, $is_enabled, $blog_id = null ) { $this->_logger->entrance(); $params = array( 'permissions' => implode( ',', $permissions ), 'is_enabled' => $is_enabled, ); $current_blog_id = get_current_blog_id(); $is_blog_switched = false; if ( is_numeric( $blog_id ) && $current_blog_id != $blog_id ) { $is_blog_switched = $this->switch_to_blog( $blog_id ); } $result = $this->api_site_call( '/permissions.json', 'put', $params ); if ( $is_blog_switched ) { $this->switch_to_blog( $current_blog_id ); } if ( ! $this->is_api_result_object( $result ) || ! isset( $result->install_id ) ) { $this->_logger->api_error( $result ); return $result; } return true; } /** * @param string[] $permissions * @param bool $is_enabled * @param bool $has_site_delegated_connection * * @return true|object `true` on success, API error object on failure. */ private function update_network_permissions( array $permissions, $is_enabled, &$has_site_delegated_connection ) { $this->_logger->entrance(); $install_id_2_blog_id = array(); $install_by_blog_id = $this->get_blog_install_map(); $has_site_delegated_connection = false; foreach ( $install_by_blog_id as $blog_id => $install ) { if ( $this->is_site_delegated_connection( $blog_id ) ) { // Only update permissions of non-delegated installs. $has_site_delegated_connection = true; continue; } $install_id_2_blog_id[ $install->id ] = $blog_id; } if ( empty( $install_id_2_blog_id ) ) { return true; } $params = array( 'permissions' => implode( ',', $permissions ), 'is_enabled' => $is_enabled, 'install_ids' => implode( ',', array_keys( $install_id_2_blog_id ) ), ); // Send update to FS. $result = $this->get_current_or_network_user_api_scope()->call( "/plugins/{$this->_module_id}/installs/permissions.json", 'put', $params ); if ( ! $this->is_api_result_object( $result, 'installs_metadata' ) ) { $this->_logger->api_error( $result ); return $result; } return true; } /** * @param mixed $result * * @return string */ private function get_api_error_message( $result ) { $error_message = sprintf( $this->get_text_inline( 'There was an unexpected API error while processing your request. Please try again in a few minutes and if it still doesn\'t work, contact the %s\'s author with the following:', 'unexpected-api-error' ), $this->_module_type ) . ' '; if ( $this->is_api_error( $result ) && isset( $result->error ) ) { $code = empty( $result->error->code ) ? '' : " Code: {$result->error->code}"; $error_message .= "{$result->error->message}{$code}"; } else { $error_message .= var_export( $result, true ); } return $error_message; } /** * @author Vova Feldman (@svovaf) * @since 2.5.1 */ function _toggle_permission_tracking_callback() { $this->_logger->entrance(); $this->check_ajax_referer( 'toggle_permission_tracking' ); if ( ! $this->is_registered( true ) ) { self::shoot_ajax_failure( 'User never opted-in.' ); } $is_enabled = fs_request_get_bool( 'is_enabled' ); $permissions = fs_request_get( 'permissions' ); if ( ! is_string( $permissions ) ) { self::shoot_ajax_failure( 'The permissions param must be a string.' ); } $permissions = explode( ',', $permissions ); $result = $this->toggle_permission_tracking( $permissions, $is_enabled ); if ( true !== $result ) { self::shoot_ajax_failure( $this->get_api_error_message( $result ) ); } self::shoot_ajax_success(); } /** * @param string[] $permissions * @param bool $is_enabled * @param int|null $blog_id * * @return bool|mixed `true` if updated successfully or no update is needed. */ private function toggle_permission_tracking( $permissions, $is_enabled, $blog_id = null ) { if ( ! $this->is_registered( true ) ) { // User never opted-in. return false; } // Check if permissions are already set as needed. if ( FS_Permission_Manager::instance( $this )->are_permissions( $permissions, $is_enabled, $blog_id ) ) { /** * Note: * When running on the network admin, there's no need to iterate through all the installs individually since network opt-in permissions are managed for ALL non-delegated installs through a single option (per permission) on the network-level storage. */ return true; } $api_managed_permissions = array_intersect( $permissions, FS_Permission_Manager::get_api_managed_permission_ids() ); if ( in_array( FS_Permission_Manager::PERMISSION_ESSENTIALS, $permissions ) && ! in_array( FS_Permission_Manager::PERMISSION_SITE, $permissions ) ) { $api_managed_permissions[] = FS_Permission_Manager::PERMISSION_SITE; } if ( ! empty( $api_managed_permissions ) ) { $has_site_delegated_connection = false; if ( ! $is_enabled && ! in_array( FS_Permission_Manager::PERMISSION_EXTENSIONS, $api_managed_permissions ) && false === FS_Permission_Manager::instance( $this )->is_extensions_tracking_allowed( $blog_id ) ) { /** * If we are turning off a permission and the extensions permission is off too, enrich the permissions update request to also turn off extensions tracking, as currently when opting in with extensions tracking disabled the extensions tracking is off but the API isn't aware of it. * * @todo Remove this entire `if` after implementing granular opt-in that also sends the permissions to the API when opting in. */ $api_managed_permissions[] = FS_Permission_Manager::PERMISSION_EXTENSIONS; } if ( is_null( $blog_id ) && fs_is_network_admin() ) { $result = $this->update_network_permissions( $api_managed_permissions, $is_enabled, $has_site_delegated_connection ); } else { $result = $this->update_site_permissions( $api_managed_permissions, $is_enabled, $blog_id ); } if ( true !== $result ) { return $result; } if ( in_array( FS_Permission_Manager::PERMISSION_SITE, $api_managed_permissions ) ) { if ( $is_enabled ) { $this->schedule_sync_cron(); } else { $this->clear_sync_cron( ! $has_site_delegated_connection ); } } if ( in_array( FS_Permission_Manager::PERMISSION_USER, $api_managed_permissions ) ) { $this->toggle_user_permission( $is_enabled, $blog_id ); } } $this->update_tracking_permissions( $permissions, $is_enabled, $blog_id ); return true; } /** * @param bool $is_enabled * @param int|null $blog_id */ private function toggle_user_permission( $is_enabled, $blog_id = null ) { $network_or_blog_ids = is_numeric( $blog_id ) ? $blog_id : fs_is_network_admin(); if ( $is_enabled ) { $this->reset_anonymous_mode( $network_or_blog_ids ); } else { $this->skip_connection( $network_or_blog_ids ); } } /** * Opt-in back into usage tracking. * * Note: This will only work if the user opted-in previously. * * Returns: * 1. FALSE - If the user never opted-in. * 2. TRUE - If successfully opted-in back to usage tracking. * 3. object - API result on failure. * * @author Leo Fajardo (@leorw) * @since 1.2.1.5 * * @bool $is_enabled * * @return bool|object */ private function toggle_site_tracking( $is_enabled, $blog_id = null ) { $this->_logger->entrance(); return $this->toggle_permission_tracking( FS_Permission_Manager::instance( $this )->get_site_tracking_permission_names(), $is_enabled, $blog_id ); } /** * If user opted-in and later disabled usage-tracking, * re-allow tracking for licensing and updates. * * @author Leo Fajardo (@leorw) * @since 1.2.1.5 * * @param bool $is_context_single_site */ private function reconnect_locally( $is_context_single_site = false ) { $this->_logger->entrance(); if ( ! $this->is_registered() ) { return; } if ( ! fs_is_network_admin() || $is_context_single_site ) { if ( $this->is_tracking_prohibited() ) { FS_Permission_Manager::instance( $this )->update_site_tracking( true ); } } else { $installs_map = $this->get_blog_install_map(); foreach ( $installs_map as $blog_id => $install ) { /** * @var FS_Site $install */ if ( ! $this->is_tracking_allowed( $blog_id, $install ) ) { FS_Permission_Manager::instance( $this )->update_site_tracking( true, $blog_id ); } } } } /** * Update permission tracking flags. When updating in a network context, in addition to updating the network-level flags, also update the permissions on the site-level for all non-delegated sites. * * @param string[] $permissions * @param bool $is_enabled * @param int|null $blog_id * * @return array */ private function update_tracking_permissions( $permissions, $is_enabled, $blog_id = null ) { // Alias. $permission_manager = FS_Permission_Manager::instance( $this ); $network_or_blog_ids = is_numeric( $blog_id ) ? $blog_id : fs_is_network_admin(); if ( true === $network_or_blog_ids ) { // Update the permission for all non-delegated sub-sites. $blog_ids = $this->get_non_delegated_blog_ids(); // Add the network-level to the array, to update the permission on the network-level storage. array_unshift( $blog_ids, null ); } else { if ( false === $network_or_blog_ids ) { $network_or_blog_ids = null; } $blog_ids = is_array( $network_or_blog_ids ) ? $network_or_blog_ids : array( $network_or_blog_ids ); } $result = array(); foreach ( $permissions as $permission ) { $permission = trim( $permission ); $is_permission_supported = true; foreach ( $blog_ids as $id ) { $is_permission_supported = $permission_manager->update_permission_tracking_flag( $permission, $is_enabled, $id ); } if ( ! $is_permission_supported ) { $permission = 'no_match'; } $result[ $permission ] = $is_enabled; } return $result; } /** * Parse plugin's settings (as defined by the plugin dev). * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @param array $plugin_info * * @throws \Freemius_Exception */ private function parse_settings( &$plugin_info ) { $this->_logger->entrance(); $id = $this->get_numeric_option( $plugin_info, 'id', false ); $public_key = $this->get_option( $plugin_info, 'public_key', false ); $secret_key = $this->get_option( $plugin_info, 'secret_key', null ); $parent_id = $this->get_numeric_option( $plugin_info, 'parent_id', null ); $parent_name = $this->get_option( $plugin_info, 'parent_name', null ); /** * @author Vova Feldman (@svovaf) * @since 1.1.9 Try to pull secret key from external config. */ if ( is_null( $secret_key ) && defined( "WP_FS__{$this->_slug}_SECRET_KEY" ) ) { $secret_key = constant( "WP_FS__{$this->_slug}_SECRET_KEY" ); } if ( isset( $plugin_info['parent'] ) ) { $parent_id = $this->get_numeric_option( $plugin_info['parent'], 'id', null ); // $parent_slug = $this->get_option( $plugin_info['parent'], 'slug', null ); // $parent_public_key = $this->get_option( $plugin_info['parent'], 'public_key', null ); // $parent_name = $this->get_option( $plugin_info['parent'], 'name', null ); } if ( false === $id ) { throw new Freemius_Exception( array( 'error' => array( 'type' => 'ParameterNotSet', 'message' => 'Plugin id parameter is not set.', 'code' => 'plugin_id_not_set', 'http' => 500, ) ) ); } if ( false === $public_key ) { throw new Freemius_Exception( array( 'error' => array( 'type' => 'ParameterNotSet', 'message' => 'Plugin public_key parameter is not set.', 'code' => 'plugin_public_key_not_set', 'http' => 500, ) ) ); } $plugin = ( $this->_plugin instanceof FS_Plugin ) ? $this->_plugin : new FS_Plugin(); $is_premium = $this->get_bool_option( $plugin_info, 'is_premium', true ); $premium_suffix = $this->get_option( $plugin_info, 'premium_suffix', '(Premium)' ); $module_type = $this->get_option( $plugin_info, 'type', $this->_module_type ); $parallel_activation = $this->get_option( $plugin_info, 'parallel_activation' ); if ( ! $is_premium && is_array( $parallel_activation ) && ( WP_FS__MODULE_TYPE_PLUGIN === $module_type ) && $this->get_bool_option( $parallel_activation, 'enabled' ) ) { $premium_basename = $this->get_option( $parallel_activation, 'premium_version_basename' ); if ( empty( $premium_basename ) ) { throw new Exception('You need to specify the premium version basename to enable parallel version activation.'); } $this->_premium_plugin_basename_from_parallel_activation = $premium_basename; if ( is_plugin_active( $premium_basename ) ) { $is_premium = true; } } $plugin->update( array( 'id' => $id, 'type' => $module_type, 'public_key' => $public_key, 'slug' => $this->_slug, 'premium_slug' => $this->get_option( $plugin_info, 'premium_slug', "{$this->_slug}-premium" ), 'parent_plugin_id' => $parent_id, 'version' => $this->get_plugin_version(), 'title' => $this->get_plugin_name( $premium_suffix ), 'file' => $this->_plugin_basename, 'is_premium' => $is_premium, 'premium_suffix' => $premium_suffix, 'is_live' => $this->get_bool_option( $plugin_info, 'is_live', true ), 'affiliate_moderation' => $this->get_option( $plugin_info, 'has_affiliation' ), 'bundle_id' => $this->get_option( $plugin_info, 'bundle_id', null ), 'bundle_public_key' => $this->get_option( $plugin_info, 'bundle_public_key', null ), 'opt_in_moderation' => $this->get_option( $plugin_info, 'opt_in', // For backward compatibility, we support both parameter names: opt_in and opt_in_moderation. $this->get_option( $plugin_info, 'opt_in_moderation', null ) ), ) ); if ( $plugin->is_updated() ) { // Update plugin details. $this->_plugin = FS_Plugin_Manager::instance( $this->_module_id )->store( $plugin ); } // Set the secret key after storing the plugin, we don't want to store the key in the storage. $this->_plugin->secret_key = $secret_key; /** * If the product is network integrated and activated and the current view is in the network level Admin dashboard, if the product's network-level menu located differently from the sub-site level, then use the network menu details (when set). * * @author Vova Feldman * @since 2.4.5 */ if ( $this->is_network_active() && fs_is_network_admin() ) { if ( isset( $plugin_info['menu_network'] ) && is_array( $plugin_info['menu_network'] ) && ! empty( $plugin_info['menu_network'] ) ) { $plugin_info['menu'] = $plugin_info['menu_network']; } } if ( ! isset( $plugin_info['menu'] ) ) { $plugin_info['menu'] = array(); if ( ! empty( $this->_storage->sdk_last_version ) && version_compare( $this->_storage->sdk_last_version, '1.1.2', '<=' ) ) { // Backward compatibility to 1.1.2 $plugin_info['menu']['slug'] = isset( $plugin_info['menu_slug'] ) ? $plugin_info['menu_slug'] : $this->_slug; } } $this->_menu = FS_Admin_Menu_Manager::instance( $this->_module_id, $this->_module_type, $this->get_unique_affix() ); $this->_menu->init( $plugin_info['menu'], $this->is_addon() ); $this->_has_addons = $this->get_bool_option( $plugin_info, 'has_addons', false ); $this->_has_paid_plans = $this->get_bool_option( $plugin_info, 'has_paid_plans', true ); $this->_has_premium_version = $this->get_bool_option( $plugin_info, 'has_premium_version', $this->_has_paid_plans ); $this->_ignore_pending_mode = $this->get_bool_option( $plugin_info, 'ignore_pending_mode', false ); $this->_is_org_compliant = $this->get_bool_option( $plugin_info, 'is_org_compliant', true ); $this->_is_premium_only = $this->get_bool_option( $plugin_info, 'is_premium_only', false ); if ( $this->_is_premium_only ) { // If premium only plugin, disable anonymous mode. $this->_enable_anonymous = false; $this->_anonymous_mode = false; } else { $this->_enable_anonymous = $this->get_bool_option( $plugin_info, 'enable_anonymous', true ); $this->_anonymous_mode = ( $this->get_bool_option( $plugin_info, 'anonymous_mode', false ) || ( $this->apply_filters( 'playground_anonymous_mode', true ) && ! empty( $_SERVER['HTTP_HOST'] ) && FS_Site::is_playground_wp_environment_by_host( $_SERVER['HTTP_HOST'] ) ) ); } $this->_permissions = $this->get_option( $plugin_info, 'permissions', array() ); $this->_is_bundle_license_auto_activation_enabled = $this->get_option( $plugin_info, 'bundle_license_auto_activation', false ); if ( ! empty( $plugin_info['trial'] ) ) { $this->_trial_days = $this->get_numeric_option( $plugin_info['trial'], 'days', // Default to 0 - trial without days specification. 0 ); $this->_is_trial_require_payment = $this->get_bool_option( $plugin_info['trial'], 'is_require_payment', false ); } $this->_navigation = $this->get_option( $plugin_info, 'navigation', $this->is_free_wp_org_theme() ? self::NAVIGATION_TABS : self::NAVIGATION_MENU ); } /** * @param string[] $options * @param string $key * @param mixed $default * * @return bool */ private function get_option( &$options, $key, $default = false ) { return ! empty( $options[ $key ] ) ? $options[ $key ] : $default; } private function get_bool_option( &$options, $key, $default = false ) { return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default; } private function get_numeric_option( &$options, $key, $default = false ) { return isset( $options[ $key ] ) && is_numeric( $options[ $key ] ) ? $options[ $key ] : $default; } /** * Gate keeper. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @return bool */ private function should_stop_execution() { if ( empty( $this->_storage->was_plugin_loaded ) ) { /** * Don't execute Freemius until plugin was fully loaded at least once, * to give the opportunity for the activation hook to run before pinging * the API for connectivity test. This logic is relevant for the * identification of new plugin install vs. plugin update. * * @author Vova Feldman (@svovaf) * @since 1.1.9 */ return true; } if ( $this->is_activation_mode() ) { if ( ! is_admin() ) { /** * If in activation mode, don't execute Freemius outside the admin dashboard. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 */ return true; } if ( ! WP_FS__IS_HTTP_REQUEST ) { /** * If in activation and executed without HTTP context (e.g. CLI, Cronjob), * then don't start Freemius. * * @author Vova Feldman (@svovaf) * @since 1.1.6.3 * * @link https://wordpress.org/support/topic/errors-in-the-freemius-class-when-running-in-wordpress-in-cli */ return true; } if ( self::is_cron() ) { /** * If in activation mode, don't execute Freemius during wp crons * (wp crons have HTTP context - called as HTTP request). * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 */ return true; } if ( self::is_ajax() ) { /** * During activation, if running in AJAX mode, unless there's a sticky * connectivity issue notice, don't run Freemius. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 */ return true; } } return false; } /** * Triggered after code type has changed. * * @author Vova Feldman (@svovaf) * @since 1.1.9.1 */ function _after_code_type_change() { $this->_logger->entrance(); if ( $this->is_theme() ) { // Expire the cache of the previous tabs since the theme may // have setting updates after code type has changed. $this->_cache->expire( 'tabs' ); $this->_cache->expire( 'tabs_stylesheets' ); } if ( ! $this->is_addon() ) { add_action( is_admin() ? 'admin_init' : 'init', array( &$this, '_plugin_code_type_changed' ) ); } if ( $this->is_registered() && $this->is_premium() ) { // Purge cached payments after switching to the premium version. // @todo This logic doesn't handle purging the cache for serviceware module upgrade. $this->get_api_user_scope()->purge_cache( "/plugins/{$this->_module_id}/payments.json?include_addons=true" ); } } /** * Handles plugin's code type change (free <--> premium). * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ function _plugin_code_type_changed() { $this->_logger->entrance(); if ( $this->is_premium() ) { $this->reconnect_locally(); // Activated premium code. $this->do_action( 'after_premium_version_activation' ); // Remove all sticky messages related to download of the premium version. $this->_admin_notices->remove_sticky( array( 'trial_started', 'plan_upgraded', 'plan_changed', 'license_activated', ) ); $notice = ''; if ( ! $this->is_only_premium() ) { $notice = sprintf( $this->get_text_inline( 'Premium %s version was successfully activated.', 'premium-activated-message' ), $this->_module_type ); } $license_notice = $this->get_license_network_activation_notice(); if ( ! empty( $license_notice ) ) { $notice .= ' ' . $license_notice; } if ( ! empty( $notice ) ) { $this->_admin_notices->add_sticky( trim( $notice ), 'premium_activated', $this->get_text_x_inline( 'W00t', 'Used to express elation, enthusiasm, or triumph (especially in electronic communication).', 'woot' ) . '!' ); } } else { // Remove sticky message related to premium code activation. $this->_admin_notices->remove_sticky( 'premium_activated' ); // Activated free code (after had the premium before). $this->do_action( 'after_free_version_reactivation' ); if ( $this->is_paying() && ! $this->is_premium() ) { $this->add_complete_upgrade_instructions_notice( sprintf( /* translators: %s: License type (e.g. you have a professional license) */ $this->get_text_inline( 'You have a %s license.', 'you-have-x-license' ), $this->get_plan_title() ), 'plan_upgraded' ); } } if ( $this->is_registered() ) { // Schedule code type changes event. $this->maybe_schedule_install_sync_cron(); } /** * Unregister the uninstall hook for the other version of the plugin (with different code type) to avoid * triggering a fatal error when uninstalling that plugin. For example, after deactivating the "free" version * of a specific plugin, its uninstall hook should be unregistered after the "premium" version has been * activated. If we don't do that, a fatal error will occur when we try to uninstall the "free" version since * the main file of the "free" version will be loaded first before calling the hooked callback. Since the * free and premium versions are almost identical (same class or have same functions), a fatal error like * "Cannot redeclare class MyClass" or "Cannot redeclare my_function()" will occur. */ $this->unregister_uninstall_hook(); $this->clear_module_main_file_cache(); // Update is_premium of latest version. $this->_storage->prev_is_premium = $this->_plugin->is_premium; } #endregion #---------------------------------------------------------------------------------- #region Add-ons #---------------------------------------------------------------------------------- /** * Check if add-on installed and activated on site. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param string|number $id_or_slug * @param bool|null $is_premium Since 1.2.1.7 can check for specified add-on version. * * @return bool */ function is_addon_activated( $id_or_slug, $is_premium = null ) { $this->_logger->entrance(); $addon_id = self::get_module_id( $id_or_slug ); $is_activated = self::has_instance( $addon_id ); if ( ! $is_activated ) { return false; } if ( is_bool( $is_premium ) ) { // Check if the specified code version is activate. $addon = $this->get_addon_instance( $addon_id ); $is_activated = ( $is_premium === $addon->is_premium() ); } return $is_activated; } /** * Check if add-on was connected to install * * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @param string|number $id_or_slug * * @return bool */ function is_addon_connected( $id_or_slug ) { $this->_logger->entrance(); $sites = self::get_all_sites( WP_FS__MODULE_TYPE_PLUGIN ); $addon_id = self::get_module_id( $id_or_slug ); $addon = $this->get_addon( $addon_id ); $slug = $addon->slug; if ( ! isset( $sites[ $slug ] ) ) { return false; } $site = $sites[ $slug ]; $plugin = FS_Plugin_Manager::instance( $addon_id )->get(); if ( $plugin->parent_plugin_id != $this->_plugin->id ) { // The given slug do NOT belong to any of the plugin's add-ons. return false; } return ( is_object( $site ) && is_numeric( $site->id ) && is_numeric( $site->user_id ) && FS_Plugin_Plan::is_valid_id( $site->plan_id ) ); } /** * Determines if add-on installed. * * NOTE: This is a heuristic and only works if the folder/file named as the slug. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param string|number $id_or_slug * * @return bool */ function is_addon_installed( $id_or_slug ) { $this->_logger->entrance(); $addon_id = self::get_module_id( $id_or_slug ); return file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $this->get_addon_basename( $addon_id ) ) ); } /** * Get add-on basename. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param string|number $id_or_slug * * @return string */ function get_addon_basename( $id_or_slug ) { $addon_id = self::get_module_id( $id_or_slug ); if ( $this->is_addon_activated( $addon_id ) ) { return self::instance( $addon_id )->get_plugin_basename(); } $addon = $this->get_addon( $addon_id ); $premium_basename = "{$addon->premium_slug}/{$addon->slug}.php"; if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $premium_basename ) ) ) { return $premium_basename; } $all_plugins = $this->get_all_plugins(); foreach ( $all_plugins as $basename => $data ) { if ( $addon->slug === $data['slug'] || $addon->premium_slug === $data['slug'] ) { return $basename; } } $free_basename = "{$addon->slug}/{$addon->slug}.php"; return $free_basename; } /** * Get installed add-ons instances. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return Freemius[] */ function get_installed_addons() { if ( $this->is_addon() ) { // Add-on cannot have add-ons. return array(); } $installed_addons = array(); foreach ( self::$_instances as $instance ) { if ( $instance->is_addon_of( $this->_plugin->id ) ) { $installed_addons[] = $instance; } } return $installed_addons; } /** * Check if any add-ons of the plugin are installed. * * @author Leo Fajardo (@leorw) * @since 1.1.1 * * @return bool */ function has_installed_addons() { if ( ! $this->has_addons() ) { return false; } foreach ( self::$_instances as $instance ) { if ( $instance->is_addon() && is_object( $instance->_parent_plugin ) ) { if ( $this->_plugin->id == $instance->_parent_plugin->id ) { return true; } } } return false; } /** * Tell Freemius that the current plugin is an add-on. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param number $parent_plugin_id The parent plugin ID */ function init_addon( $parent_plugin_id ) { $this->_plugin->parent_plugin_id = $parent_plugin_id; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool */ function is_addon() { return ( isset( $this->_plugin->parent_plugin_id ) && is_numeric( $this->_plugin->parent_plugin_id ) ); } /** * @author Vova Feldman (@svovaf) * @since 2.3.2 * * @param number $parent_product_id * * @return bool */ function is_addon_of( $parent_product_id ) { return ( $this->is_addon() && $parent_product_id == $this->_plugin->parent_plugin_id ); } /** * Deactivate add-on if it's premium only and the user does't have a valid license. * * @param bool $is_after_trial_cancel * * @return bool If add-on was deactivated. */ private function deactivate_premium_only_addon_without_license( $is_after_trial_cancel = false ) { if ( ! $this->has_free_plan() && ! $this->has_features_enabled_license() && ! $this->_has_premium_license() ) { if ( $this->is_registered() ) { // IF wrapper is turned off because activation_timestamp is currently only stored for plugins (not addons). // if (empty($this->_storage->activation_timestamp) || // (WP_FS__SCRIPT_START_TIME - $this->_storage->activation_timestamp) > 30 // ) { /** * @todo When it's first fail, there's no reason to try and re-sync because the licenses were just synced after initial activation. * * Retry syncing the user add-on licenses. */ // Sync licenses. $this->_sync_licenses(); // } // Try to activate premium license. $this->_activate_license( true ); } if ( ! $this->has_free_plan() && ! $this->has_features_enabled_license() && ! $this->_has_premium_license() ) { // @todo Check if deactivate plugins also call the deactivation hook. $this->_parent->_admin_notices->add_sticky( sprintf( ( $is_after_trial_cancel ? $this->_parent->get_text_inline( '%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you\'ll have to purchase a license.', 'addon-trial-cancelled-message' ) : $this->_parent->get_text_inline( '%s is a premium only add-on. You have to purchase a license first before activating the plugin.', 'addon-no-license-message' ) ), '' . $this->_plugin->title . '' ) . ' ' . sprintf( '%s  ➜', $this->_parent->addon_url( $this->_slug ), esc_attr( sprintf( $this->_parent->get_text_inline( 'More information about %s', 'more-information-about-x' ), $this->_plugin->title ) ), $this->_parent->get_text_inline( 'Purchase License', 'purchase-license' ) ), 'no_addon_license_' . $this->_slug, ( $is_after_trial_cancel ? '' : $this->_parent->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...' ), ( $is_after_trial_cancel ? 'success' : 'error' ) ); deactivate_plugins( array( $this->_plugin_basename ), true ); return true; } } return false; } #endregion #---------------------------------------------------------------------------------- #region Sandbox #---------------------------------------------------------------------------------- /** * Set Freemius into sandbox mode for debugging. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param string $secret_key */ function init_sandbox( $secret_key ) { $this->_plugin->secret_key = $secret_key; // Update plugin details. FS_Plugin_Manager::instance( $this->_module_id )->update( $this->_plugin, true ); } /** * Check if running payments in sandbox mode. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @return bool */ function is_payments_sandbox() { return ( ! $this->is_live() ) || isset( $this->_plugin->secret_key ); } #endregion /** * Check if running test vs. live plugin. * * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @return bool */ function is_live() { return $this->_plugin->is_live; } /** * Check if super-admin skipped connection for all sites in the network. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function is_network_anonymous() { if ( ! $this->_is_network_active ) { return false; } $is_anonymous_ms = $this->_storage->get( 'is_anonymous_ms' ); if ( empty( $is_anonymous_ms ) ) { return false; } return $is_anonymous_ms['is']; } /** * Check if super-admin opted-in for all sites in the network. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function is_network_connected() { if ( ! $this->_is_network_active ) { return false; } return $this->_storage->get( 'is_network_connected' ); } /** * Check if the user skipped connecting the account with Freemius. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return bool */ function is_anonymous() { if ( ! isset( $this->_is_anonymous ) ) { if ( $this->is_network_anonymous() ) { $this->_is_anonymous = true; } else if ( fs_is_network_admin() ) { /** * When not-network-anonymous, yet, running in the network admin, consider as anonymous only when ALL non-delegated sites are set to anonymous. */ $non_delegated_sites = $this->get_non_delegated_blog_ids(); foreach ( $non_delegated_sites as $blog_id ) { $is_anonymous = $this->_storage->get( 'is_anonymous', false, $blog_id ); if ( empty( $is_anonymous ) || false === $is_anonymous[ 'is' ] ) { $this->_is_anonymous = false; break; } } if ( false !== $this->_is_anonymous ) { $this->_is_anonymous = true; } } else { if ( ! isset( $this->_storage->is_anonymous ) ) { // Not skipped. $this->_is_anonymous = false; } else if ( is_bool( $this->_storage->is_anonymous ) ) { // For back compatibility, since the variable was boolean before. $this->_is_anonymous = $this->_storage->is_anonymous; // Upgrade stored data format to 1.1.3 format. $this->set_anonymous_mode( $this->_storage->is_anonymous ); } else { // Version 1.1.3 and later. $this->_is_anonymous = $this->_storage->is_anonymous['is']; } } } return $this->_is_anonymous; } /** * Check if the user skipped the connection of a specified site. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id * * @return bool */ function is_anonymous_site( $blog_id = 0 ) { if ( $this->is_network_anonymous() ) { return true; } $is_anonymous = $this->_storage->get( 'is_anonymous', false, $blog_id ); if ( empty( $is_anonymous ) ) { return false; } return $is_anonymous['is']; } /** * Check if user connected his account and install pending email activation. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return bool */ function is_pending_activation() { return $this->_storage->get( 'is_pending_activation', false ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ private function clear_pending_activation_mode() { // Remove the pending activation sticky notice (if it still exists). $this->_admin_notices->remove_sticky( 'activation_pending' ); // Clear the plugin's pending activation mode. unset( $this->_storage->is_pending_activation ); } /** * Check if plugin must be WordPress.org compliant. * * @since 1.0.7 * * @return bool */ function is_org_repo_compliant() { return $this->_is_org_compliant; } #-------------------------------------------------------------------------------- #region WP Cron Common #-------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * * @return object */ private function get_cron_data( $name ) { $this->_logger->entrance( $name ); /** * @var object $cron_data */ return $this->_storage->get( "{$name}_cron", null ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. */ private function clear_cron_data( $name ) { $this->_logger->entrance( $name ); $this->_storage->remove( "{$name}_cron" ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * @param int $cron_blog_id The cron executing blog ID. */ private function set_cron_data( $name, $cron_blog_id = 0 ) { $this->_logger->entrance( $name ); $this->_storage->store( "{$name}_cron", (object) array( 'version' => $this->get_plugin_version(), 'blog_id' => $cron_blog_id, 'sdk_version' => $this->version, 'timestamp' => WP_FS__SCRIPT_START_TIME, 'on' => true, ) ); } /** * Get the cron's executing blog ID. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * * @return int */ private function get_cron_blog_id( $name ) { $this->_logger->entrance( $name ); if ( ! is_multisite() ) { // Not a multisite. return 0; } $cron_data = $this->get_cron_data( $name ); return ( is_object( $cron_data ) && is_numeric( $cron_data->blog_id ) ) ? $cron_data->blog_id : 0; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * * @return bool */ private function is_cron_on( $name ) { $this->_logger->entrance( $name ); /** * @var object $cron_data */ $cron_data = $this->get_cron_data( $name ); return ( ! is_null( $cron_data ) && true === $cron_data->on ); } /** * Unix timestamp for previous cron execution or false if never executed. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * * @return int|false */ private function cron_last_execution( $name ) { $this->_logger->entrance( $name ); return $this->_storage->get( "{$name}_timestamp" ); } /** * Set cron execution time to now. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. */ private function set_cron_execution_timestamp( $name ) { $this->_logger->entrance( $name ); $this->_storage->store( "{$name}_timestamp", time() ); } /** * Sets the keepalive time to now. * * @author Leo Fajardo (@leorw) * @since 2.2.3 * * @param bool|null $use_network_level_storage */ private function set_keepalive_timestamp( $use_network_level_storage = null ) { $this->_logger->entrance(); $this->_storage->store( 'keepalive_timestamp', time(), $use_network_level_storage ); } /** * Check if cron was executed in the last $period of seconds. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * @param int $period In seconds * * @return bool */ private function is_cron_executed( $name, $period = WP_FS__TIME_24_HOURS_IN_SEC ) { $this->_logger->entrance( $name ); $last_execution = $this->cron_last_execution( $name ); if ( ! is_numeric( $last_execution ) ) { return false; } return ( $last_execution > ( WP_FS__SCRIPT_START_TIME - $period ) ); } /** * WP Cron is executed on a site level. When running in a multisite network environment * with the network integration activated, for optimization reasons, we are consolidating * the installs data sync cron to be executed only from a single site. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $except_blog_id Target any except the excluded blog ID. * * @return int */ private function get_cron_target_blog_id( $except_blog_id = 0 ) { if ( ! is_multisite() ) { return 0; } if ( $this->_is_network_active ) { $network_install_blog_id = $this->_storage->network_install_blog_id; if ( is_numeric( $network_install_blog_id ) && $except_blog_id != $network_install_blog_id && self::is_site_active( $network_install_blog_id ) ) { // Try to run cron from the main network blog. $install = $this->get_install_by_blog_id( $network_install_blog_id ); if ( is_object( $install ) && $this->is_tracking_allowed( $network_install_blog_id, $install ) ) { return $network_install_blog_id; } } } // Get first opted-in blog ID with active tracking. $installs = $this->get_blog_install_map(); foreach ( $installs as $blog_id => $install ) { if ( $except_blog_id != $blog_id && self::is_site_active( $blog_id ) && $this->is_tracking_allowed( $blog_id, $install ) ) { return $blog_id; } } return 0; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * @param string $action_tag Callback action tag. * @param bool $is_network_clear If set to TRUE, clear sync cron even if there are installs that are still connected. */ private function clear_cron( $name, $action_tag = '', $is_network_clear = false ) { $this->_logger->entrance( $name ); if ( ! $this->is_cron_on( $name ) ) { return; } $clear_cron = true; if ( ! $is_network_clear && $this->_is_network_active ) { $installs = $this->get_blog_install_map(); foreach ( $installs as $blog_id => $install ) { /** * @var FS_Site $install */ if ( $this->is_tracking_allowed( $blog_id, $install ) ) { $clear_cron = false; break; } } } if ( ! $clear_cron ) { return; } $cron_blog_id = $this->get_cron_blog_id( $name ); $this->clear_cron_data( $name ); if ( 0 < $cron_blog_id ) { switch_to_blog( $cron_blog_id ); } if ( empty( $action_tag ) ) { $action_tag = $name; } wp_clear_scheduled_hook( $this->get_action_tag( $action_tag ) ); if ( 0 < $cron_blog_id ) { restore_current_blog(); } } /** * Unix timestamp for next cron execution or false if not scheduled. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * @param string $action_tag Callback action tag. * * @return int|false */ private function get_next_scheduled_cron( $name, $action_tag = '' ) { $this->_logger->entrance( $name ); if ( ! $this->is_cron_on( $name ) ) { return false; } $cron_blog_id = $this->get_cron_blog_id( $name ); if ( 0 < $cron_blog_id ) { switch_to_blog( $cron_blog_id ); } if ( empty( $action_tag ) ) { $action_tag = $name; } $next_scheduled = wp_next_scheduled( $this->get_action_tag( $action_tag ) ); if ( 0 < $cron_blog_id ) { restore_current_blog(); } return $next_scheduled; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * @param string $action_tag Callback action tag. * @param string $recurrence 'single' or 'daily'. * @param int $start_at Defaults to now. * @param bool $randomize_start If true, schedule first job randomly during the next 12 hours. Otherwise, schedule job to start right away. * @param int $except_blog_id Target any except the excluded blog ID. */ private function schedule_cron( $name, $action_tag = '', $recurrence = 'single', $start_at = WP_FS__SCRIPT_START_TIME, $randomize_start = true, $except_blog_id = 0 ) { $this->_logger->entrance( $name ); $this->clear_cron( $name, $action_tag, true ); $cron_blog_id = $this->get_cron_target_blog_id( $except_blog_id ); if ( is_multisite() && 0 == $cron_blog_id ) { // Don't schedule cron since couldn't find a target blog. return; } if ( 0 < $cron_blog_id ) { switch_to_blog( $cron_blog_id ); } if ( 'daily' === $recurrence ) { if ( $randomize_start ) { // Schedule first sync with a random 12 hour time range from now. $start_at += rand( 0, ( WP_FS__TIME_24_HOURS_IN_SEC / 2 ) ); } // Schedule daily WP cron. wp_schedule_event( $start_at, 'daily', $this->get_action_tag( $action_tag ) ); } else if ( 'single' === $recurrence ) { // Schedule single cron. wp_schedule_single_event( $start_at, $this->get_action_tag( $action_tag ) ); } $this->set_cron_data( $name, $cron_blog_id ); if ( 0 < $cron_blog_id ) { restore_current_blog(); } } /** * Consolidated cron execution for performance optimization. The max number of API requests is based on the number of unique opted-in users. * that doesn't halt page loading. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string $name Cron name. * @param callable $callable The function that should be executed. */ private function execute_cron( $name, $callable ) { $this->_logger->entrance( $name ); // Store the last time data sync was executed. $this->set_cron_execution_timestamp( $name ); // Check if API is temporary down. if ( FS_Api::is_temporary_down() ) { return; } // @todo Add logic that identifies API latency, and reschedule the next background sync randomly between 8-16 hours. $users_2_blog_ids = array(); if ( ! is_multisite() ) { // Add dummy blog. $users_2_blog_ids[0] = array( 0 ); } else { $installs = $this->get_blog_install_map(); foreach ( $installs as $blog_id => $install ) { if ( $this->is_tracking_allowed( $blog_id, $install ) ) { if ( ! isset( $users_2_blog_ids[ $install->user_id ] ) ) { $users_2_blog_ids[ $install->user_id ] = array(); } $users_2_blog_ids[ $install->user_id ][] = $blog_id; } } } $current_blog_id = get_current_blog_id(); foreach ( $users_2_blog_ids as $user_id => $blog_ids ) { if ( 0 < $blog_ids[0] ) { $this->switch_to_blog( $blog_ids[0] ); } call_user_func_array( $callable, array( $blog_ids, ( is_multisite() ? $current_blog_id : null ) ) ); foreach ( $blog_ids as $blog_id ) { $this->do_action( "after_{$name}_cron", $blog_id ); } } if ( is_multisite() ) { $this->switch_to_blog( $current_blog_id, fs_is_network_admin() ? $this->get_network_install() : null ); $this->do_action( "after_{$name}_cron_multisite" ); } } #endregion #---------------------------------------------------------------------------------- #region Daily Sync Cron #---------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return bool */ private function is_sync_cron_scheduled() { return $this->is_cron_on( 'sync' ); } /** * Get the sync cron's executing blog ID. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return int */ private function get_sync_cron_blog_id() { return $this->get_cron_blog_id( 'sync' ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.3 */ private function run_manual_sync() { if ( ! $this->is_user_admin() ) { return; } // Run manual sync. $this->_sync_cron(); // Reschedule next cron to run 24 hours from now (performance optimization). $this->schedule_sync_cron( time() + WP_FS__TIME_24_HOURS_IN_SEC, false ); } /** * Data sync cron job. Replaces the background sync non blocking HTTP request * that doesn't halt page loading. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * @since 2.0.0 Consolidate all the data sync into the same cron for performance optimization. The max number of API requests is based on the number of unique opted-in users. */ function _sync_cron() { $this->_logger->entrance(); $this->execute_cron( 'sync', array( &$this, '_sync_cron_method' ) ); } /** * The actual data sync cron logic. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int[] $blog_ids * @param int|null $current_blog_id @since 2.2.3. This is passed from the `execute_cron` method and used by the * `_sync_plugin_license` method in order to switch to the previous blog when sending * updates for a single site in case `execute_cron` has switched to a different blog. */ function _sync_cron_method( array $blog_ids, $current_blog_id = null ) { if ( $this->is_registered() ) { if ( $this->has_paid_plan() ) { // Initiate background plan sync. $this->_sync_license( true, false, $current_blog_id ); if ( $this->is_paying() ) { // Check for premium plugin updates. $this->check_updates( true ); } } else { // Sync install(s) (only if something changed locally). if ( 1 < count( $blog_ids ) ) { $this->sync_installs(); } else { $this->sync_install(); } $this->maybe_sync_install_user(); } } } /** * Check if sync was executed in the last $period of seconds. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @param int $period In seconds * * @return bool */ private function is_sync_executed( $period = WP_FS__TIME_24_HOURS_IN_SEC ) { return $this->is_cron_executed( 'sync', $period ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @return bool */ private function is_sync_cron_on() { return $this->is_cron_on( 'sync' ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ private function maybe_schedule_sync_cron() { $next_schedule = $this->next_sync_cron(); // The event is properly scheduled, so no need to reschedule it. if ( is_numeric( $next_schedule ) && $next_schedule > time() ) { return; } $this->schedule_sync_cron(); } /** * Instead of running blocking install sync event, execute non blocking scheduled cron job. * * @param int $except_blog_id Since 2.0.0 when running in a multisite network environment, the cron execution is consolidated. This param allows excluding specified blog ID from being the cron job executor. * * @author Leo Fajardo (@leorw) * @since 2.9.1 */ private function maybe_schedule_install_sync_cron( $except_blog_id = 0 ) { if ( ! $this->is_user_in_admin() ) { return; } if ( $this->is_clone() ) { return; } if ( // The event has been properly scheduled, so no need to reschedule it. is_numeric( $this->next_install_sync() ) ) { return; } $this->schedule_cron( 'install_sync', 'install_sync', 'single', WP_FS__SCRIPT_START_TIME, false, $except_blog_id ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @param int $start_at Defaults to now. * @param bool $randomize_start If true, schedule first job randomly during the next 12 hours. Otherwise, schedule job to start right away. * @param int $except_blog_id Since 2.0.0 when running in a multisite network environment, the cron execution is consolidated. This param allows excluding excluded specified blog ID from being the cron executor. */ private function schedule_sync_cron( $start_at = WP_FS__SCRIPT_START_TIME, $randomize_start = true, $except_blog_id = 0 ) { $this->schedule_cron( 'sync', 'data_sync', 'daily', $start_at, $randomize_start, $except_blog_id ); } /** * Add the actual sync function to the cron job hook. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 */ private function hook_callback_to_sync_cron() { $this->add_action( 'data_sync', array( &$this, '_sync_cron' ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @param bool $is_network_clear Since 2.0.0 If set to TRUE, clear sync cron even if there are installs that are still connected. */ private function clear_sync_cron( $is_network_clear = false ) { $this->_logger->entrance(); $this->clear_cron( 'sync', 'data_sync', $is_network_clear ); } /** * Unix timestamp for next sync cron execution or false if not scheduled. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @return int|false */ function next_sync_cron() { return $this->get_next_scheduled_cron( 'sync', 'data_sync' ); } /** * Unix timestamp for previous sync cron execution or false if never executed. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @return int|false */ function last_sync_cron() { return $this->cron_last_execution( 'sync' ); } #endregion Daily Sync Cron ------------------------------------------------------------------ #---------------------------------------------------------------------------------- #region Async Install Sync #---------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @return bool */ private function is_install_sync_scheduled() { return $this->is_cron_on( 'install_sync' ); } /** * Get the sync cron's executing blog ID. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return int */ private function get_install_sync_cron_blog_id() { return $this->get_cron_blog_id( 'install_sync' ); } /** * Unix timestamp for previous install sync cron execution or false if never executed. * * @todo There's some very strange bug that $this->_storage->install_sync_timestamp value is not being updated. But for sure the sync event is working. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @return int|false */ function last_install_sync() { return $this->cron_last_execution( 'install_sync' ); } /** * Unix timestamp for next install sync cron execution or false if not scheduled. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @return int|false */ function next_install_sync() { return $this->get_next_scheduled_cron( 'install_sync', 'install_sync' ); } /** * Add the actual install sync function to the cron job hook. * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 */ private function hook_callback_to_install_sync() { $this->add_action( 'install_sync', array( &$this, '_run_sync_install' ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @param bool $is_network_clear Since 2.0.0 If set to TRUE, clear sync cron even if there are installs that are still connected. */ private function clear_install_sync_cron( $is_network_clear = false ) { $this->_logger->entrance(); $this->clear_cron( 'install_sync', 'install_sync', $is_network_clear ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * @since 2.0.0 Consolidate all the data sync into the same cron for performance optimization. The max number of API requests is based on the number of unique opted-in users. */ public function _run_sync_install() { $this->_logger->entrance(); $this->execute_cron( 'sync', array( &$this, '_sync_install_cron_method' ) ); } /** * The actual install(s) sync cron logic. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int[] $blog_ids * @param int|null $current_blog_id */ function _sync_install_cron_method( array $blog_ids, $current_blog_id = null ) { if ( $this->is_registered() ) { if ( 1 < count( $blog_ids ) ) { $this->sync_installs( array(), true ); } else { $this->sync_install( array(), true ); } $this->maybe_sync_install_user(); } } #endregion Async Install Sync ------------------------------------------------------------------ /** * Show a notice that activation is currently pending. * * @todo Add some sort of mechanism to allow users to update the email address they would like to opt-in with when $is_suspicious_email is true. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param bool|string $email_address * @param bool $is_pending_trial Since 1.2.1.5 * @param bool $is_suspicious_email Since 2.5.0 Set to true when there's an indication that email address the user opted in with is fake/dummy/placeholder. * @param bool $has_upgrade_context Since 2.5.3 * @param bool $support_email_address Since 2.5.3 */ function _add_pending_activation_notice( $email_address = false, $is_pending_trial = false, $is_suspicious_email = false, $has_upgrade_context = false, $support_email_address = false ) { if ( ! is_string( $email_address ) ) { $current_user = self::_get_current_wp_user(); $email_address = $current_user->user_email; } $formatted_message_args = array( "{$this->get_plugin_name()}", "{$email_address}", ); if ( ! $has_upgrade_context || ! fs_is_network_admin() ) { /* translators: %3$s: action (e.g.: "start the trial" or "complete the opt-in") */ $formatted_message = $this->get_text_inline( 'You should receive a confirmation email for %1$s to your mailbox at %2$s. Please make sure you click the button in that email to %3$s.', 'pending-activation-message' ); $formatted_message_args[] = $is_pending_trial ? $this->get_text_inline( 'start the trial', 'start-the-trial' ) : $this->get_text_inline( 'complete the opt-in', 'complete-the-opt-in' ); $notice_title = $this->get_text_inline( 'Thanks!', 'thanks' ); } else { /* translators: %3$s: What the user is expected to receive via email (e.g.: "the installation instructions" or "a license key") */ $formatted_message = $this->get_text_inline( 'You should receive %3$s for %1$s to your mailbox at %2$s in the next 5 minutes.' ); if ( $this->has_release_on_freemius() ) { $formatted_message_args[] = $this->get_text_x_inline( 'the installation instructions', 'Part of the message telling the user what they should receive via email.', 'the-installation-instructions-phrase' ); } else { $formatted_message_args[] = $this->get_text_x_inline( 'a license key', 'Part of the message telling the user what they should receive via email.', 'a-license-key-phrase' ); $formatted_message .= ( ' ' . sprintf( /* translators: %s: activation link (e.g.: Click here) */ $this->get_text_inline( '%s to activate the license once you get it.', 'license-activation-link-message' ), sprintf( '%s', $this->get_activation_url( array( 'fs_action' => 'reset_pending_activation_mode', 'require_license' => 'true', 'fs_unique_affix' => $this->get_unique_affix(), ) ), $this->get_text_x_inline( 'Click here', 'Part of an activation link message.', 'click-here' ) ) ) ); } $formatted_message_args[] = ( ! empty( $support_email_address ) ) ? ( "{$support_email_address}" ) : $this->get_text_x_inline( "the product's support email address", 'Part of the message that tells the user to check their spam folder for a specific email.', 'product-support-email-address-phrase' ); $formatted_message .= ( ' ' . $this->get_text_inline( 'If you didn\'t get the email, try checking your spam folder or search for emails from %4$s.', 'check-spam-folder-message' ) ); $notice_title = $this->get_text_inline( 'Thanks for upgrading.', 'after-upgrade-thank-you-message' ); } $this->_admin_notices->add_sticky( vsprintf( $formatted_message, $formatted_message_args ), 'activation_pending', $notice_title ); } /** * Check if currently in plugin activation. * * @author Vova Feldman (@svovaf) * @since 1.1.4 * * @return bool */ function is_plugin_activation() { $result = get_transient( "fs_{$this->_module_type}_{$this->_slug}_activated" ); return !empty($result); } /** * * NOTE: admin_menu action executed before admin_init. * * @author Vova Feldman (@svovaf) * @since 1.0.7 */ function _admin_init_action() { $is_migration = $this->is_migration(); /** * Automatically redirect to connect/activation page after plugin activation. * * @since 1.1.7 Do NOT redirect to opt-in when running in network admin mode. */ if ( $this->is_plugin_activation() ) { delete_transient( "fs_{$this->_module_type}_{$this->_slug}_activated" ); if ( isset( $_GET['activate-multi'] ) ) { /** * Don't redirect if activating multiple plugins at once (bulk activation). */ } else if ( self::is_deactivation_snoozed() && ( // Either running the free code base. ! $this->is_premium() || // Or if has a free version. ! $this->is_only_premium() || // If premium only, don't redirect if license is activated. ( $this->is_registered() && ! $this->can_use_premium_code() ) ) ) { /** * Don't redirect if activating during the deactivation snooze period (aka troubleshooting), unless activating a paid product version that the admin didn't enter its license key yet. */ } else if ( ! $is_migration ) { $this->_redirect_on_activation_hook(); return; } } if ( $is_migration ) { return; } if ( fs_request_is_action( $this->get_unique_affix() . '_skip_activation' ) ) { check_admin_referer( $this->get_unique_affix() . '_skip_activation' ); $this->skip_connection( fs_is_network_admin() ); fs_redirect( $this->get_after_activation_url( 'after_skip_url' ) ); } if ( $this->is_network_activation_mode() && fs_request_is_action( $this->get_unique_affix() . '_delegate_activation' ) ) { check_admin_referer( $this->get_unique_affix() . '_delegate_activation' ); $this->delegate_connection(); fs_redirect( $this->get_after_activation_url( 'after_delegation_url' ) ); } $this->_add_upgrade_action_link(); if ( ! ( ! $this->_is_network_active && fs_is_network_admin() ) && ( ( true === $this->_storage->require_license_activation ) || // Not registered nor anonymous. ( ! $this->is_registered() && ! $this->is_anonymous() ) || // OR, network level and in network upgrade mode. ( fs_is_network_admin() && $this->_is_network_active && $this->is_network_upgrade_mode() ) ) ) { if ( ! $this->is_pending_activation() ) { if ( ! $this->is_activation_page() ) { /** * If a user visits any other admin page before activating the premium-only theme with a valid * license, reactivate the previous theme. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ if ( $this->is_theme() && ! $this->has_settings_menu() && ! isset( $_REQUEST['fs_action'] ) && $this->can_activate_previous_theme() ) { if ( $this->is_only_premium() ) { $this->activate_previous_theme(); return; } if ( true === $this->_storage->require_license_activation ) { $this->_storage->require_license_activation = false; } } if ( ! fs_is_network_admin() && $this->is_network_activation_mode() && ! $this->is_delegated_connection() ) { return; } if ( $this->is_plugin_new_install() || $this->is_only_premium() ) { if ( ! $this->_anonymous_mode && ( ! $this->is_addon() || ! $this->_parent->is_anonymous() ) ) { // Show notice for new plugin installations. $this->_admin_notices->add( sprintf( $this->get_text_inline( 'You are just one step away - %s', 'you-are-step-away' ), sprintf( '%s', $this->get_activation_url( array(), ! $this->is_delegated_connection() ), sprintf( $this->get_text_x_inline( 'Complete "%s" Activation Now', '%s - plugin name. As complete "PluginX" activation now', 'activate-x-now' ), $this->get_plugin_name() ) ) ), '', 'update-nag' ); } } else { if ( $this->should_add_sticky_optin_notice() ) { $this->add_sticky_optin_admin_notice(); } if ( $this->has_filter( 'optin_pointer_element' ) ) { // Don't show admin nag if plugin update. wp_enqueue_script( 'wp-pointer' ); wp_enqueue_style( 'wp-pointer' ); $this->_enqueue_connect_essentials(); add_action( 'admin_print_footer_scripts', array( $this, '_add_connect_pointer_script' ) ); } } } } if ( $this->show_opt_in_on_themes_page() && $this->is_activation_page() ) { $this->_show_theme_activation_optin_dialog(); } } } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return bool */ private function should_add_sticky_optin_notice() { if ( $this->is_addon() && $this->_parent->is_anonymous() ) { return false; } if ( fs_is_network_admin() ) { if ( ! $this->_is_network_active ) { return false; } if ( ! $this->is_network_activation_mode() ) { return false; } return ! isset( $this->_storage->sticky_optin_added_ms ); } if ( ! $this->is_activation_mode() ) { return false; } // If running from a blog admin and delegated the connection. return ! isset( $this->_storage->sticky_optin_added ); } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 */ private function add_sticky_optin_admin_notice() { if ( ! $this->_is_network_active || ! fs_is_network_admin() ) { $this->_storage->sticky_optin_added = true; } else { $this->_storage->sticky_optin_added_ms = true; } // Show notice for new plugin installations. $this->_admin_notices->add_sticky( sprintf( $this->get_text_inline( 'We made a few tweaks to the %s, %s', 'few-plugin-tweaks' ), $this->_module_type, sprintf( '%s', $this->get_activation_url(), sprintf( $this->get_text_inline( 'Opt in to make "%s" better!', 'optin-x-now' ), $this->get_plugin_name() ) ) ), 'connect_account', '', 'update-nag' ); } /** * Enqueue connect requires scripts and styles. * * @author Vova Feldman (@svovaf) * @since 1.1.4 */ function _enqueue_connect_essentials() { wp_enqueue_script( 'jquery' ); wp_enqueue_script( 'json2' ); fs_enqueue_local_script( 'postmessage', 'nojquery.ba-postmessage.js' ); fs_enqueue_local_script( 'fs-postmessage', 'postmessage.js' ); } /** * Add connect / opt-in pointer. * * @author Vova Feldman (@svovaf) * @since 1.1.4 */ function _add_connect_pointer_script() { $vars = array( 'id' => $this->_module_id ); $pointer_content = fs_get_template( 'connect.php', $vars ); ?> _menu->get_raw_slug() ) || fs_is_plugin_page( $this->_slug ); } /* Events ------------------------------------------------------------------------------------------------------------------*/ /** * Delete site install from Database. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param bool $store * @param int|null $blog_id Since 2.0.0 * * @return false|int The install ID if deleted. Otherwise, FALSE (when install not exist). */ function _delete_site( $store = true, $blog_id = null ) { return self::_delete_site_by_slug( $this->_slug, $this->_module_type, $store, $blog_id ); } /** * Delete site install from Database. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @param string $slug * @param string $module_type * @param bool $store * @param int|null $blog_id Since 2.0.0 * * @return false|int The install ID if deleted. Otherwise, FALSE (when install not exist). */ static function _delete_site_by_slug( $slug, $module_type, $store = true, $blog_id = null ) { $sites = self::get_all_sites( $module_type, $blog_id ); $install_id = false; if ( isset( $sites[ $slug ] ) ) { if ( is_object( $sites[ $slug ] ) ) { $install_id = $sites[ $slug ]->id; } unset( $sites[ $slug ] ); self::set_account_option_by_module( $module_type, 'sites', $sites, $store, $blog_id ); } return $install_id; } /** * Delete plugin's plans information. * * @param bool $store Flush to Database if true. * @param bool $keep_associated_plans If set to false, delete all plans, even if a plan is associated with an install. * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ private function _delete_plans( $store = true, $keep_associated_plans = true ) { $this->_logger->entrance(); $plans = self::get_all_plans( $this->_module_type ); $plans_to_keep = array(); if ( $keep_associated_plans ) { $plans_ids_to_keep = $this->get_plans_ids_associated_with_installs(); foreach ( $plans_ids_to_keep as $plan_id ) { $plan = self::_get_plan_by_id( $plan_id ); if ( is_object( $plan ) ) { $plans_to_keep[] = self::_encrypt_entity( $plan ); } } } if ( ! empty( $plans_to_keep ) ) { $plans[ $this->_slug ] = $plans_to_keep; } else { unset( $plans[ $this->_slug ] ); } $this->set_account_option( 'plans', $plans, $store ); } /** * Delete all plugin licenses. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param bool $store */ private function _delete_licenses( $store = true ) { $this->_logger->entrance(); $all_licenses = self::get_all_licenses(); unset( $all_licenses[ $this->_module_id ] ); self::$_accounts->set_option( 'all_licenses', $all_licenses, $store ); } /** * Check if Freemius was added on new plugin installation. * * @author Vova Feldman (@svovaf) * @since 1.1.5 * * @return bool */ function is_plugin_new_install() { return isset( $this->_storage->is_plugin_new_install ) && $this->_storage->is_plugin_new_install; } /** * Check if it's the first plugin release that is running Freemius. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @return bool */ function is_first_freemius_powered_version() { return empty( $this->_storage->plugin_last_version ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return bool|string */ private function get_previous_theme_slug() { return isset( $this->_storage->previous_theme ) ? $this->_storage->previous_theme : false; } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return bool */ private function can_activate_previous_theme() { return $this->can_activate_theme( $this->get_previous_theme_slug() ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @return bool */ private function can_activate_theme( $slug ) { if ( false !== $slug && current_user_can( 'switch_themes' ) ) { $theme_instance = wp_get_theme( $slug ); return $theme_instance->exists(); } return false; } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 */ private function activate_previous_theme() { switch_theme( $this->get_previous_theme_slug() ); unset( $this->_storage->previous_theme ); global $pagenow; if ( 'themes.php' === $pagenow ) { /** * Refresh the active theme information. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ fs_redirect( $this->admin_url( $pagenow ) ); } } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return string */ function get_previous_theme_activation_url() { if ( ! $this->can_activate_previous_theme() ) { return ''; } /** * Activation URL * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ return wp_nonce_url( $this->admin_url( 'themes.php?action=activate&stylesheet=' . urlencode( $this->get_previous_theme_slug() ) ), 'switch-theme_' . $this->get_previous_theme_slug() ); } /** * Saves the slug of the previous theme if it still exists so that it can be used by the logic in the opt-in * form that decides whether to add a close button to the opt-in dialog or not. So after a premium-only theme is * activated, the close button will appear and will reactivate the previous theme if clicked. If the previous * theme doesn't exist, then there will be no close button. * * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @param string $slug_or_name Old theme's slug or name. * @param bool|WP_Theme $old_theme WP_Theme instance of the old theme if it still exists. */ function _activate_theme_event_hook( $slug_or_name, $old_theme = false ) { $this->_storage->previous_theme = ( false !== $old_theme ) ? $old_theme->get_stylesheet() : $slug_or_name; $this->_activate_plugin_event_hook(); } /** * Plugin activated hook. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @uses FS_Api */ function _activate_plugin_event_hook() { $this->_logger->entrance( 'slug = ' . $this->_slug ); if ( ! $this->is_user_admin() ) { return; } $this->unregister_uninstall_hook(); // Clear API cache on activation. FS_Api::clear_cache(); $is_premium_version_activation = $this->is_plugin() ? ( current_filter() !== ( 'activate_' . $this->_free_plugin_basename ) ) : $this->is_premium(); if ( $is_premium_version_activation && $this->is_pending_activation() ) { $this->clear_pending_activation_mode(); } $this->_logger->info( 'Activating ' . ( $is_premium_version_activation ? 'premium' : 'free' ) . ' plugin version.' ); if ( $this->is_plugin() ) { // This logic is relevant only to plugins since both the free and premium versions of a plugin can be active at the same time. // 1. If running in the activation of the FREE module, get the basename of the PREMIUM. // 2. If running in the activation of the PREMIUM module, get the basename of the FREE. $other_version_basename = $is_premium_version_activation ? $this->_free_plugin_basename : $this->premium_plugin_basename(); if ( ! $this->_is_network_active ) { /** * Themes are always network activated, but the ACTUAL activation is per site. * * During the activation, the plugin isn't yet active, therefore, * _is_network_active will be set to false even if it's a network level * activation. So we need to fix that by looking at the is_network_admin() value. * * @author Vova Feldman */ $this->_is_network_active = ( $this->_is_multisite_integrated && fs_is_network_admin() ); } /** * If the other module version is active, deactivate it. * * is_plugin_active() checks if the plugin is active on the site or the network level and * deactivate_plugins() deactivates the plugin whether it's activated on the site or network level. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ if ( is_plugin_active( $other_version_basename ) && $this->apply_filters( 'deactivate_on_activation', ! $this->is_parallel_activation() ) ) { deactivate_plugins( $other_version_basename ); } } if ( $this->is_registered() ) { if ( $is_premium_version_activation ) { $this->reconnect_locally(); } // Schedule re-activation event and sync. // $this->sync_install( array(), true ); $this->maybe_schedule_install_sync_cron(); // If activating the premium module version, add an admin notice to congratulate for an upgrade completion. if ( $is_premium_version_activation ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'The upgrade of %s was successfully completed.', 'successful-version-upgrade-message' ), sprintf( '%s', $this->_plugin->title ) ), $this->get_text_x_inline( 'W00t', 'Used to express elation, enthusiasm, or triumph (especially in electronic communication).', 'woot' ) . '!' ); } } else if ( $this->is_anonymous() ) { if ( isset( $this->_storage->is_anonymous_ms ) && $this->_storage->is_anonymous_ms['is'] ) { $plugin_version = $this->_storage->is_anonymous_ms['version']; $network = true; } else { $plugin_version = isset( $this->_storage->is_anonymous ) ? $this->_storage->is_anonymous['version'] : null; $network = false; } /** * Reset "skipped" click cache on the following: * 1. Freemius DEV mode. * 2. WordPress DEBUG mode. * 3. If a plugin and the user skipped the exact same version before. * * @since 1.2.2.7 Ulrich Pogson (@grapplerulrich) asked to not reset the SKIPPED flag if the exact same THEME version was activated before unless the developer is running with WP_DEBUG on, or Freemius debug mode on (WP_FS__DEV_MODE). * * @todo 4. If explicitly asked to retry after every activation. */ if ( WP_FS__DEV_MODE || ( ( $this->is_plugin() || ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) && $this->get_plugin_version() == $plugin_version ) ) { $this->reset_anonymous_mode( $network ); } } $is_trial_or_has_features_enabled_license = ( $this->is_trial() || $this->has_features_enabled_license() ); if ( $this->is_addon() && ! $is_trial_or_has_features_enabled_license ) { /** * When activating an add-on, try to also activate a license. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ if ( ! $this->_is_network_active ) { $this->maybe_activate_addon_license(); } else { $this->maybe_network_activate_addon_license(); } /** * Avoid redirecting to the license activation screen after automatically activating an add-on license. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $is_trial_or_has_features_enabled_license = ( $this->is_trial() || $this->has_features_enabled_license() ); if ( $is_trial_or_has_features_enabled_license && true === $this->_storage->require_license_activation ) { $this->_storage->require_license_activation = false; } } if ( $is_premium_version_activation && ( ( ! $this->is_registered() && $this->is_anonymous() ) || ( $this->is_registered() && ! $is_trial_or_has_features_enabled_license ) ) ) { $this->_storage->require_license_activation = true; } if ( ! isset( $this->_storage->is_plugin_new_install ) ) { /** * If no previous version of plugin's version exist, it means that it's either * the first time that the plugin installed on the site, or the plugin was installed * before but didn't have Freemius integrated. * * Since register_activation_hook() do NOT fires on updates since 3.1, and only fires * on manual activation via the dashboard, is_plugin_activation() is TRUE * only after immediate activation. * * @since 1.1.4 * @link https://make.wordpress.org/core/2010/10/27/plugin-activation-hooks-no-longer-fire-for-updates/ */ $this->_storage->is_plugin_new_install = empty( $this->_storage->plugin_last_version ); } /** * Also flush when activating the premium version so that even if Freemius was off before, the API * connectivity test can be run again. * * @author Leo Fajardo (@leorw) * @since 2.2.3.1 */ $has_api_connectivity = $this->has_api_connectivity( WP_FS__DEV_MODE || $is_premium_version_activation ); if ( ! $this->_anonymous_mode && ( false !== $has_api_connectivity ) && ! $this->_isAutoInstall ) { // Store hint that the plugin was just activated to enable auto-redirection to settings. set_transient( "fs_{$this->_module_type}_{$this->_slug}_activated", true, 60 ); } /** * Activation hook is executed after the plugin's main file is loaded, therefore, * after the plugin was loaded. The logic is located at activate_plugin() * ./wp-admin/includes/plugin.php. * * @author Vova Feldman (@svovaf) * @since 1.1.9 */ $this->_storage->was_plugin_loaded = true; } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 */ private function maybe_activate_addon_license() { $parent_fs = $this->get_parent_instance(); if ( ! is_object( $parent_fs ) || ( ! $parent_fs->is_registered() && ! $parent_fs->is_network_registered() ) ) { // Try to activate a license only if the parent plugin is active and has a valid `install`. return; } $license = $this->get_active_parent_license(); if ( ! is_object( $license ) ) { return; } if ( $this->is_bundle_license_auto_activation_enabled() && ! empty( $license->products ) ) { $this->activate_bundle_license( $license ); return; } if ( ! $this->is_registered() ) { // Opt in with a license key. $this->opt_in( $parent_fs->get_current_or_network_user()->email, false, false, $license->secret_key ); } else { // Activate the license. $install = $this->api_site_call( '/', 'put', array( 'license_key' => $this->apply_filters( 'license_key', $license->secret_key ) ) ); if ( ! FS_Api::is_api_error( $install ) ) { $this->_sync_addon_license( $this->get_id(), true ); } } } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param FS_Plugin_License $license */ private function maybe_network_activate_addon_license( $license = null ) { $parent_fs = $this->get_parent_instance(); if ( ! is_object( $parent_fs ) || ( ! $parent_fs->is_registered() && ! $parent_fs->is_network_registered() ) ) { // Try to activate a license only if the parent plugin is active and has a valid `install`. return; } $license = ( ! is_null( $license ) ) ? $license : $this->get_active_parent_license(); if ( ! is_object( $license ) ) { return; } if ( $this->is_bundle_license_auto_activation_enabled() && ! empty( $license->products ) ) { $this->activate_bundle_license( $license ); return; } if ( ! $this->is_network_registered() ) { $sites = $this->get_sites_for_network_level_optin(); if ( count( $sites ) > $license->left() ) { // If the add-on is network active, try to activate the license only if it can be activated on all sites. return; } // Opt in with a license key. $this->opt_in( $parent_fs->get_user()->email, false, false, $license->secret_key, false, false, false, null, $sites ); } else { $blog_2_install_map = array(); $site_ids = array(); $all_sites = Freemius::get_sites(); foreach ( $all_sites as $site ) { $blog_id = Freemius::get_site_blog_id( $site ); $install = $this->get_install_by_blog_id( $blog_id ); if ( is_object( $install ) && FS_Plugin_License::is_valid_id( $install->license_id ) ) { // Skip license activation for installs that are already associated with a license. continue; } if ( is_object( $install ) ) { $blog_2_install_map[ $blog_id ] = $install; } else { $site_ids[] = $blog_id; } } if ( ( count( $blog_2_install_map ) + count( $site_ids ) ) > $license->left() ) { return; } $user = $this->get_current_or_network_user(); if ( ! empty( $blog_2_install_map ) ) { $result = $this->activate_license_on_many_installs( $user, $license->secret_key, $blog_2_install_map ); if ( true !== $result ) { return; } } if ( ! empty( $site_ids ) ) { $this->activate_license_on_many_sites( $user, $license->secret_key, $site_ids ); } } } /** * Tries to activate a bundle license for all supported products if the current product is activated with a bundle license. This is called after activating an available license (not via the license activation dialog but by clicking on a license activation button) for a product via its "Account" page. * * @author Leo Fajardo (@leorw) * @since 2.4.0 * * @param FS_Plugin_License $license * @param array $sites * @param int $blog_id */ private function maybe_activate_bundle_license( FS_Plugin_License $license = null, $sites = array(), $blog_id = 0 ) { if ( ! is_object( $license ) && $this->has_active_valid_license() ) { $license = $this->_license; } if ( ! is_object( $license ) ) { return; } $parent_license = ( ! empty( $license->products ) ) ? $license : $this->get_active_parent_license( $license->secret_key ); if ( is_object( $parent_license ) ) { $this->activate_bundle_license( $parent_license, $sites, $blog_id ); } } /** * Try to activate a bundle license for all the bundle products installed on the site. * (1) If a child product install already has a license, the bundle license won't be activated. * (2) On multi-site networks, if the attempt to activate the bundle license is triggered from the network admin, the bundle license activation will only work for non-delegated sites and only if none of them is associated with a license. Even if one of the sites has the product installed with a license key, skip the bundle license activation for the product. * (3) On multi-site networks, if the attempt to activate the bundle license is triggered from a site-level admin, only activate the license if the product is site-level activated or delegated, and the product installation is not yet associated with a license. * * @author Leo Fajardo (@leorw) * @since 2.4.0 * * @param FS_Plugin_License $license * @param array $sites * @param int $current_blog_id */ private function activate_bundle_license( $license, $sites = array(), $current_blog_id = 0 ) { $is_network_admin = fs_is_network_admin(); $installs_by_blog_map = array(); $site_info_by_blog_map = array(); /** * Try to activate the license for all supported products. * * @author Leo Fajardo */ foreach ( $license->products as $product_id ) { $fs = self::get_instance_by_id( $product_id ); if ( ! is_object( $fs ) ) { continue; } if ( ! $fs->has_paid_plan() ) { continue; } if ( ! $fs->is_addon() && ! FS_Plan_Manager::instance()->has_paid_plan( $fs->_plans ) ) { /** * The parent product can be free-only but can have its `has_paid_plan` flag set to `true` when * there is a context bundle. */ continue; } if ( $current_blog_id > 0 ) { $fs->switch_to_blog( $current_blog_id ); } if ( $fs->has_active_valid_license() ) { continue; } if ( ! $is_network_admin || $current_blog_id > 0 ) { if ( $fs->is_network_active() && ! $fs->is_delegated_connection( $current_blog_id ) ) { // Do not try to activate the license in the site level if the product is network active and the connection was not delegated. continue; } } else { if ( ! $fs->is_network_active() ) { // Do not try to activate the license in the network level if the product is not network active. continue; } if ( $fs->is_network_delegated_connection() ) { // Do not try to activate the license in the network level if the activation has been delegated to site admins. continue; } $has_install_with_license = false; // Collection of sites that have an install entity that is not activated with a license or non-delegated sites that have no install entity, or both types of site. $filtered_sites = array(); if ( empty( $sites ) ) { $all_sites = self::get_sites(); foreach ( $all_sites as $site ) { $sites[] = array( 'blog_id' => self::get_site_blog_id( $site ) ); } } else { // Populate the map here to avoid calling `$fs->get_site_info( $site );` in the other `for` loop below. foreach ( $sites as $site ) { if ( ! isset( $site['blog_id'] ) || ! is_numeric( $site['blog_id'] ) ) { continue; } $site_info_by_blog_map[ $site['blog_id'] ] = $site; } } foreach ( $sites as $site ) { if ( ! isset( $site['blog_id'] ) || ! is_numeric( $site['blog_id'] ) ) { continue; } $blog_id = $site['blog_id']; if ( ! isset( $installs_by_blog_map[ $blog_id ] ) ) { $installs_by_blog_map[ $blog_id ] = self::get_all_sites( $fs->get_module_type(), $blog_id ); } $installs = $installs_by_blog_map[ $blog_id ]; $install = null; if ( isset( $installs[ $fs->get_slug() ] ) ) { $install = $installs[ $fs->get_slug() ]; if ( is_object( $install ) && ( ! FS_Site::is_valid_id( $install->id ) || ! FS_User::is_valid_id( $install->user_id ) || ! FS_Plugin_Plan::is_valid_id( $install->plan_id ) ) ) { $install = null; } } if ( is_object( $install ) && FS_Plugin_License::is_valid_id( $install->license_id ) ) { $has_install_with_license = true; break; } if ( $fs->is_site_delegated_connection( $blog_id ) ) { // Site activation delegated, don't activate bundle license on the site in the network admin. continue; } if ( ! isset( $site_info_by_blog_map[ $blog_id ] ) ) { $site_info_by_blog_map[ $blog_id ] = $fs->get_site_info( $site ); } $filtered_sites[] = $site_info_by_blog_map[ $blog_id ]; } if ( $has_install_with_license || empty( $filtered_sites ) ) { // Do not try to activate the license at the network level if there's any install with a license or there's no site to activate the license on. continue; } $sites = $filtered_sites; } $fs->activate_migrated_license( $license->secret_key, null, null, $sites, ( $current_blog_id > 0 ? $current_blog_id : null ) ); } } /** * Returns a parent license that can be activated for the context product. * * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param string|null $license_key * @param bool $flush * * @return FS_Plugin_License */ function get_active_parent_license( $license_key = null, $flush = true ) { $parent_licenses_endpoint = "/plugins/{$this->get_id()}/parent_licenses.json?filter=activatable"; $fs = $this; if ( $this->is_addon() ) { $parent_instance = $this->get_parent_instance(); if ( is_object( $parent_instance ) && $parent_instance->is_registered() ) { $fs = $parent_instance; } } $foreign_licenses = $fs->get_foreign_licenses_info( self::get_all_licenses( $this->get_parent_id() ) ); if ( ! empty ( $foreign_licenses ) ) { $foreign_licenses = array( // Prefix with `+` to tell the server to include foreign licenses in the licenses collection. 'ids' => ( urlencode( '+' ) . implode( ',', $foreign_licenses['ids'] ) ), 'license_keys' => implode( ',', array_map( 'urlencode', $foreign_licenses['license_keys'] ) ) ); $parent_licenses_endpoint = add_query_arg( $foreign_licenses, $parent_licenses_endpoint ); } $result = $fs->get_current_or_network_user_api_scope()->get( $parent_licenses_endpoint, $flush ); if ( ! $this->is_api_result_object( $result, 'licenses' ) || ! is_array( $result->licenses ) || empty( $result->licenses ) ) { return null; } $parent_license = null; if ( empty( $license_key ) ) { $parent_license = $result->licenses[0]; } else { foreach ( $result->licenses as $license ) { if ( $license_key === $license->secret_key ) { $parent_license = $license; break; } } } if ( ! is_null( $parent_license ) ) { $parent_license = new FS_Plugin_License( $parent_license ); } return $parent_license; } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @return array */ function get_sites_for_network_level_optin() { $sites = array(); $all_sites = self::get_sites(); foreach ( $all_sites as $site ) { $blog_id = self::get_site_blog_id( $site ); if ( ! $this->is_site_delegated_connection( $blog_id ) && ! $this->is_installed_on_site( $blog_id ) ) { $sites[] = $this->get_site_info( $site ); } } return $sites; } /** * Delete account. * * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @param bool $check_user Enforce checking if user have plugins activation privileges. */ function delete_account_event( $check_user = true ) { $this->_logger->entrance( 'slug = ' . $this->_slug ); if ( $check_user && ! $this->is_user_admin() ) { return; } $this->do_action( 'before_account_delete' ); // Clear all admin notices. $this->_admin_notices->clear_all_sticky( false ); $this->_delete_site( false ); $delete_network_common_data = true; if ( $this->_is_network_active ) { $installs = $this->get_blog_install_map(); // Don't delete common network data unless no other installs left. $delete_network_common_data = empty( $installs ); } if ( $delete_network_common_data ) { $this->_delete_plans( false ); $this->_delete_licenses( false ); // Delete add-ons related to plugin's account. $this->_delete_account_addons( false ); } // @todo Delete plans and licenses of add-ons. self::$_accounts->store(); /** * IMPORTANT: * Clear crons must be executed before clearing all storage. * Otherwise, the cron will not be cleared. */ if ( $delete_network_common_data ) { $this->clear_sync_cron(); } $this->clear_install_sync_cron(); // Clear all storage data. $this->_storage->clear_all( true, array( 'is_delegated_connection', 'connectivity_test', 'is_on', ), false ); // Send delete event. $this->get_api_site_scope()->call( '/', 'delete' ); $this->do_action( 'after_account_delete' ); } /** * Delete network level account. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param bool $check_user Enforce checking if user have plugins activation privileges. */ function delete_network_account_event( $check_user = true ) { $this->_logger->entrance( 'slug = ' . $this->_slug ); if ( $check_user && ! $this->is_user_admin() ) { return; } $this->do_action( 'before_network_account_delete' ); // Clear all admin notices. $this->_admin_notices->clear_all_sticky(); $this->_delete_plans( false, false ); $this->_delete_licenses( false ); // Delete add-ons related to plugin's account. $this->_delete_account_addons( false ); // @todo Delete plans and licenses of add-ons. self::$_accounts->store( true ); /** * IMPORTANT: * Clear crons must be executed before clearing all storage. * Otherwise, the cron will not be cleared. */ $this->clear_sync_cron( true ); $this->clear_install_sync_cron( true ); $sites = self::get_sites(); $install_ids = array(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); if ( $this->is_site_delegated_connection( $blog_id ) ) { continue; } $install_id = $this->_delete_site( true, $blog_id ); // Clear all storage data. $this->_storage->clear_all( true, array( 'connectivity_test' ), $blog_id ); if ( FS_Site::is_valid_id( $install_id ) ) { $install_ids[] = $install_id; } switch_to_blog( $blog_id ); $this->do_action( 'after_account_delete' ); restore_current_blog(); } $this->_storage->clear_all( true, array( 'connectivity_test', 'is_on', ), true ); // Send delete event. if ( ! empty( $install_ids ) ) { $result = $this->get_current_or_network_user_api_scope()->call( "/plugins/{$this->_module_id}/installs.json?ids=" . implode( ',', $install_ids ), 'delete' ); } $this->do_action( 'after_network_account_delete' ); } /** * Plugin deactivation hook. * * @author Vova Feldman (@svovaf) * @since 1.0.1 */ function _deactivate_plugin_hook() { $this->_logger->entrance( 'slug = ' . $this->_slug ); if ( ! $this->is_user_admin() ) { return; } $is_network_deactivation = fs_is_network_admin(); $storage_keys_for_removal = array(); $this->_admin_notices->clear_all_sticky(); $storage_keys_for_removal[] = 'sticky_optin_added'; if ( isset( $this->_storage->sticky_optin_added ) ) { unset( $this->_storage->sticky_optin_added ); } if ( ! isset( $this->_storage->is_plugin_new_install ) ) { // Remember that plugin was already installed. $this->_storage->is_plugin_new_install = false; } // Hook to plugin uninstall. register_uninstall_hook( $this->_plugin_main_file_path, array( 'Freemius', '_uninstall_plugin_hook' ) ); $this->clear_module_main_file_cache(); $this->clear_sync_cron( $this->_is_network_active ); $this->clear_install_sync_cron(); if ( $this->is_registered() ) { if ( $this->is_premium() && ! $this->has_active_valid_license() ) { FS_Plugin_Updater::instance( $this )->delete_update_data(); } if ( $is_network_deactivation ) { // Send deactivation event. $this->sync_installs( array( 'is_active' => false, ) ); } else { // Send deactivation event. $this->sync_install( array( 'is_active' => false, ) ); } } else { if ( false === $this->has_api_connectivity() && ! $this->is_premium() ) { // Reset connectivity test cache. $this->clear_connectivity_info(); $storage_keys_for_removal[] = 'connectivity_test'; } } if ( $is_network_deactivation ) { if ( isset( $this->_storage->sticky_optin_added_ms ) ) { unset( $this->_storage->sticky_optin_added_ms ); } if ( ! empty( $storage_keys_for_removal ) ) { $sites = self::get_sites(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); foreach ( $storage_keys_for_removal as $key ) { $this->_storage->remove( $key, false, $blog_id ); } $this->_storage->save( $blog_id ); } } } // Clear API cache on deactivation. FS_Api::clear_cache(); $this->remove_sdk_reference(); } /** * @author Vova Feldman (@svovaf) * @since 1.1.6 */ private function remove_sdk_reference() { global $fs_active_plugins; foreach ( $fs_active_plugins->plugins as $sdk_path => $data ) { if ( $this->_plugin_basename == $data->plugin_path ) { unset( $fs_active_plugins->plugins[ $sdk_path ] ); break; } } fs_fallback_to_newest_active_sdk(); } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param bool $is_anonymous * @param bool|int $network_or_blog_id Since 2.0.0 */ private function set_anonymous_mode( $is_anonymous = true, $network_or_blog_id = 0 ) { // Store information regarding skip to try and opt-in the user // again in the future. $skip_info = array( 'is' => $is_anonymous, 'timestamp' => WP_FS__SCRIPT_START_TIME, 'version' => $this->get_plugin_version(), ); if ( true === $network_or_blog_id ) { $this->_storage->is_anonymous_ms = $skip_info; } else { $this->_storage->store( 'is_anonymous', $skip_info, $network_or_blog_id ); } $this->network_upgrade_mode_completed(); // Update anonymous mode cache. $this->_is_anonymous = $is_anonymous; } /** * @author Vova Feldman (@svovaf) * @since 2.5.1 * * @param bool|int $network_or_blog_id */ private function unset_anonymous_mode( $network_or_blog_id = 0 ) { if ( true === $network_or_blog_id ) { unset( $this->_storage->is_anonymous_ms ); } else { $this->_storage->remove( 'is_anonymous', true, $network_or_blog_id ); } } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id Site ID. * @param int $user_id User ID. * @param string $domain Site domain. * @param string $path Site path. * @param int $network_id Network ID. Only relevant on multi-network installations. * @param array $meta Metadata. Used to set initial site options. * * @uses Freemius::is_license_network_active() to check if the context license was network activated by the super-admin. * @uses Freemius::is_network_connected() to check if the super-admin network opted-in. * @uses Freemius::is_network_anonymous() to check if the super-admin network skipped. * @uses Freemius::is_network_delegated_connection() to check if the super-admin network delegated the connection to the site admins. */ public function _after_new_blog_callback( $blog_id, $user_id, $domain, $path, $network_id, $meta ) { $this->_logger->entrance(); if ( ! $this->_is_network_active ) { FS_Clone_Manager::instance()->store_blog_install_info( $blog_id ); return; } $site = null; $new_blog_id = $blog_id; if ( $this->is_premium() && $this->is_network_connected() && is_object( $this->_license ) && $this->_license->can_activate( FS_Site::is_localhost_by_address( $domain ) ) && $this->is_license_network_active( $blog_id ) ) { /** * Running the premium version, the license was network activated, and the license can also be activated on the current site -> so try to opt-in with the license key. */ $current_blog_id = get_current_blog_id(); $license = clone $this->_license; $this->switch_to_blog( $blog_id ); // Opt-in with network user. $this->install_with_user( $this->get_network_user(), $license->secret_key, false, false, false ); if ( is_object( $this->_site ) ) { if ( $this->_site->license_id == $license->id ) { /** * If the license was activated successfully, sync the license data from the remote server. */ $this->_license = $license; $this->sync_site_license(); } } $site = $this->_site; $this->switch_to_blog( $current_blog_id ); if ( is_object( $site ) ) { FS_Clone_Manager::instance()->store_blog_install_info( $blog_id, $site ); // Already connected (with or without a license), so no need to continue. return; } } if ( $this->is_network_anonymous() ) { /** * Opt-in was network skipped so automatically skip the opt-in for the new site. */ $this->skip_site_connection( $blog_id ); } else if ( $this->is_network_delegated_connection() ) { /** * Opt-in was network delegated so automatically delegate the opt-in for the new site's admin. */ $this->delegate_site_connection( $blog_id ); } else if ( $this->is_network_connected() ) { /** * Opt-in was network activated so automatically opt-in with the network user and new site admin. */ $current_blog_id = get_current_blog_id(); $this->switch_to_blog( $blog_id ); // Opt-in with network user. $this->install_with_user( $this->get_network_user(), false, false, false, false ); $site = $this->_site; $this->switch_to_blog( $current_blog_id ); } else { /** * If the super-admin mixed different options (connect, skip, delegated): * a) If at least one site connection was delegated, then automatically delegate connection. * b) Otherwise, it means that at least one site was skipped and at least one site was connected. For a simplified UX in the initial release of the multisite network integration, skip the connection for the newly created site. If the super-admin will want to opt-in they can still do that from the network level Account page. */ $has_delegated_site = false; $sites = self::get_sites(); foreach ( $sites as $wp_site ) { $blog_id = self::get_site_blog_id( $wp_site ); if ( $this->is_site_delegated_connection( $blog_id ) ) { $has_delegated_site = true; break; } } if ( $has_delegated_site ) { $this->delegate_site_connection( $blog_id ); } else { $this->skip_site_connection( $blog_id ); } } /** * Store the new blog's information even if there's no install so that when a clone install is stored in the new blog's storage, we can try to resolve it automatically. * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ FS_Clone_Manager::instance()->store_blog_install_info( $new_blog_id, $site ); } /** * @author Vova Feldman (@svovaf) * @since 2.5.0 * * @param \WP_Site $new_site * @param array $args */ public function _after_wp_initialize_site_callback( WP_Site $new_site, $args ) { $this->_logger->entrance(); $this->_after_new_blog_callback( $new_site->id, // Dummy user ID (not in use). 0, $new_site->domain, $new_site->path, $new_site->network_id, // Dummy meta, not in use. array() ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param bool|int|int[] $network_or_blog_ids Since 2.0.0. */ private function reset_anonymous_mode( $network_or_blog_ids = false ) { if ( true === $network_or_blog_ids ) { $this->unset_anonymous_mode( true ); if ( fs_is_network_admin() ) { $this->_is_anonymous = null; } // Rest anonymous mode for all non-delegated sub-sites. $blog_ids = $this->get_non_delegated_blog_ids(); } else { if ( false === $network_or_blog_ids ) { $network_or_blog_ids = 0; } $blog_ids = is_array( $network_or_blog_ids ) ? $network_or_blog_ids : array( $network_or_blog_ids ); foreach ( $blog_ids as $blog_id ) { if ( 0 === $blog_id || get_current_blog_id() == $blog_id ) { $this->_is_anonymous = null; } } } foreach ( $blog_ids as $blog_id ) { $this->unset_anonymous_mode( $blog_id ); } /** * Ensure that this field is also "false", otherwise, if the current module's type is "theme" and the module * has no menus, the opt-in popup will not be shown immediately (in this case, the user will have to click * on the admin notice that contains the opt-in link in order to trigger the opt-in popup). * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ if ( ! $this->_is_network_active ) { $this->_is_anonymous = null; } } /** * @author Leo Fajardo (@leorw) * @since 2.5.3 */ private function update_license_required_permissions_if_anonymous() { if ( ! $this->is_anonymous() ) { return; } $this->reset_anonymous_mode( fs_is_network_admin() ); FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array( 'essentials' => true, 'events' => true, 'diagnostic' => false, 'extensions' => false, 'site' => false, ) ); } /** * This is used to ensure that before redirecting to the opt-in page after resetting the anonymous mode or * deleting the account in the network level, the URL of the page to redirect to is correct. * * @author Leo Fajardo (@leorw) * * @since 2.1.3 */ private function maybe_set_slug_and_network_menu_exists_flag() { if ( ! empty( $this->_dynamically_added_top_level_page_hook_name ) ) { $this->_menu->set_slug_and_network_menu_exists_flag( $this->_menu->has_menu() ? $this->_menu->get_slug() : $this->_slug ); } } /** * Clears the anonymous mode and redirects to the opt-in screen. * * @author Vova Feldman (@svovaf) * @since 1.1.7 */ function connect_again() { if ( ! $this->is_anonymous() && ! $this->is_pending_activation() ) { return; } if ( $this->is_anonymous() ) { $this->reset_anonymous_mode( fs_is_network_admin() ); } $activation_url_params = array(); if ( $this->is_pending_activation() ) { $this->clear_pending_activation_mode(); if ( fs_request_get_bool( 'require_license' ) ) { $activation_url_params['require_license'] = true; } } $this->maybe_set_slug_and_network_menu_exists_flag(); fs_redirect( $this->get_activation_url( $activation_url_params ) ); } /** * Skip account connect, and set anonymous mode. * * @author Vova Feldman (@svovaf) * @since 1.1.1 * * @param bool|int|int[] $network_or_blog_ids Since 2.5.1 */ function skip_connection( $network_or_blog_ids = false ) { $this->_logger->entrance(); $this->_admin_notices->remove_sticky( 'connect_account' ); if ( true === $network_or_blog_ids ) { $this->set_anonymous_mode( true, true ); if ( fs_is_network_admin() ) { $this->_is_anonymous = null; } // Rest anonymous mode for all non-delegated sub-sites. $blog_ids = $this->get_non_delegated_blog_ids(); } else { if ( false === $network_or_blog_ids ) { $network_or_blog_ids = 0; } $blog_ids = is_array( $network_or_blog_ids ) ? $network_or_blog_ids : array( $network_or_blog_ids ); foreach ( $blog_ids as $blog_id ) { if ( 0 === $blog_id || get_current_blog_id() == $blog_id ) { $this->_is_anonymous = null; } } } foreach ( $blog_ids as $blog_id ) { $this->skip_site_connection( $blog_id ); } $this->network_upgrade_mode_completed(); } /** * Skip connection for specific site in the network. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int|null $blog_id * @param bool $send_skip */ private function skip_site_connection( $blog_id = null ) { $this->_logger->entrance(); $this->_admin_notices->remove_sticky( 'connect_account', $blog_id ); $this->set_anonymous_mode( true, $blog_id ); } /** * Plugin version update hook. * * @author Vova Feldman (@svovaf) * @since 1.0.4 */ private function update_plugin_version_event() { $this->_logger->entrance(); if ( ! $this->is_registered() ) { return; } $this->maybe_schedule_install_sync_cron(); // $this->sync_install( array(), true ); } /** * Generate an MD5 signature of a plugins collection. * This helper methods used to identify changes in a plugins collection. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param array [string]array $plugins * * @return string */ private function get_plugins_thumbprint( $plugins ) { ksort( $plugins ); $thumbprint = ''; foreach ( $plugins as $basename => $data ) { $thumbprint .= $data['slug'] . ',' . $data['Version'] . ',' . ( $data['is_active'] ? '1' : '0' ) . ';'; } return md5( $thumbprint ); } /** * Return a list of modified plugins since the last sync. * * Note: * There's no point to store a plugins counter since even if the number of * plugins didn't change, we still need to check if the versions are all the * same and the activity state is similar. * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @return array|false */ private function get_plugins_data_for_api() { // Alias. $site_active_plugins_option_name = 'active_plugins'; $network_plugins_option_name = 'all_plugins'; /** * Collection of all site level active plugins. */ $site_active_plugins_cache = self::$_accounts->get_option( $site_active_plugins_option_name ); if ( ! is_object( $site_active_plugins_cache ) ) { $site_active_plugins_cache = (object) array( 'timestamp' => '', 'md5' => '', 'plugins' => array(), ); } $time = time(); if ( ! empty( $site_active_plugins_cache->timestamp ) && ( $time - $site_active_plugins_cache->timestamp ) < WP_FS__TIME_5_MIN_IN_SEC ) { // Don't send plugin updates if last update was in the past 5 min. return false; } // Write timestamp to lock the logic. $site_active_plugins_cache->timestamp = $time; self::$_accounts->set_option( $site_active_plugins_option_name, $site_active_plugins_cache, true ); // Reload options from DB. self::$_accounts->load( true ); $site_active_plugins_cache = self::$_accounts->get_option( $site_active_plugins_option_name ); if ( $time != $site_active_plugins_cache->timestamp ) { // If timestamp is different, then another thread captured the lock. return false; } /** * Collection of all plugins (network level). */ $network_plugins_cache = self::$_accounts->get_option( $network_plugins_option_name ); if ( ! is_object( $network_plugins_cache ) ) { $network_plugins_cache = (object) array( 'timestamp' => '', 'md5' => '', 'plugins' => array(), ); } // Check if there's a change in plugins. $network_plugins = self::get_network_plugins(); $site_active_plugins = self::get_site_active_plugins(); $network_plugins_thumbprint = $this->get_plugins_thumbprint( $network_plugins ); $site_active_plugins_thumbprint = $this->get_plugins_thumbprint( $site_active_plugins ); // Check if plugins status changed (version or active/inactive). $network_plugins_changed = ( $network_plugins_cache->md5 !== $network_plugins_thumbprint ); $site_active_plugins_changed = ( $site_active_plugins_cache->md5 !== $site_active_plugins_thumbprint ); if ( ! $network_plugins_changed && ! $site_active_plugins_changed ) { // No changes. return array(); } $plugins_update_data = array(); foreach ( $network_plugins_cache->plugins as $basename => $data ) { if ( ! isset( $network_plugins[ $basename ] ) ) { // Plugin uninstalled. $uninstalled_plugin_data = $data; $uninstalled_plugin_data['is_active'] = false; $uninstalled_plugin_data['is_uninstalled'] = true; $plugins_update_data[] = $uninstalled_plugin_data; unset( $network_plugins[ $basename ] ); unset( $network_plugins_cache->plugins[ $basename ] ); unset( $site_active_plugins_cache->plugins[ $basename ] ); continue; } $was_active = $data['is_active'] || ( isset( $site_active_plugins_cache->plugins[ $basename ] ) && true === $site_active_plugins_cache->plugins[ $basename ]['is_active'] ); $is_active = $network_plugins[ $basename ]['is_active'] || ( isset( $site_active_plugins[ $basename ] ) && $site_active_plugins[ $basename ]['is_active'] ); if ( ! isset( $site_active_plugins_cache->plugins[ $basename ] ) && isset( $site_active_plugins[ $basename ] ) ) { // Plugin was site level activated. $site_active_plugins_cache->plugins[ $basename ] = $network_plugins[ $basename ]; $site_active_plugins_cache->plugins[ $basename ]['is_active'] = true; } else if ( isset( $site_active_plugins_cache->plugins[ $basename ] ) && ! isset( $site_active_plugins[ $basename ] ) ) { // Plugin was site level deactivated. unset( $site_active_plugins_cache->plugins[ $basename ] ); } $prev_version = $data['version']; $current_version = $network_plugins[ $basename ]['Version']; if ( $was_active !== $is_active || $prev_version !== $current_version ) { // Plugin activated or deactivated, or version changed. if ( $was_active !== $is_active ) { if ( $data['is_active'] != $network_plugins[ $basename ]['is_active'] ) { $network_plugins_cache->plugins[ $basename ]['is_active'] = $data['is_active']; } } if ( $prev_version !== $current_version ) { $network_plugins_cache->plugins[ $basename ]['Version'] = $current_version; } $updated_plugin_data = $data; $updated_plugin_data['is_active'] = $is_active; $updated_plugin_data['version'] = $current_version; $updated_plugin_data['title'] = $network_plugins[ $basename ]['Name']; $plugins_update_data[] = $updated_plugin_data; } } // Find new plugins that weren't yet seen before. foreach ( $network_plugins as $basename => $data ) { if ( ! isset( $network_plugins_cache->plugins[ $basename ] ) ) { // New plugin. $new_plugin = array( 'slug' => $data['slug'], 'version' => $data['Version'], 'title' => $data['Name'], 'is_active' => $data['is_active'], 'is_uninstalled' => false, ); $network_plugins_cache->plugins[ $basename ] = $new_plugin; $is_site_level_active = ( isset( $site_active_plugins[ $basename ] ) && $site_active_plugins[ $basename ]['is_active'] ); /** * If not network active, set the activity status based on the site-level plugin status. */ if ( ! $new_plugin['is_active'] ) { $new_plugin['is_active'] = $is_site_level_active; } $plugins_update_data[] = $new_plugin; if ( isset( $site_active_plugins[ $basename ] ) ) { $site_active_plugins_cache->plugins[ $basename ] = $new_plugin; $site_active_plugins_cache->plugins[ $basename ]['is_active'] = $is_site_level_active; } } } $site_active_plugins_cache->md5 = $site_active_plugins_thumbprint; $site_active_plugins_cache->timestamp = $time; self::$_accounts->set_option( $site_active_plugins_option_name, $site_active_plugins_cache, true ); $network_plugins_cache->md5 = $network_plugins_thumbprint; $network_plugins_cache->timestamp = $time; self::$_accounts->set_option( $network_plugins_option_name, $network_plugins_cache, true ); return $plugins_update_data; } /** * Return a list of modified themes since the last sync. * * Note: * There's no point to store a themes counter since even if the number of * themes didn't change, we still need to check if the versions are all the * same and the activity state is similar. * * @author Vova Feldman (@svovaf) * @since 1.1.8 * * @return array|false */ private function get_themes_data_for_api() { // Alias. $option_name = 'all_themes'; $all_cached_themes = self::$_accounts->get_option( $option_name ); if ( ! is_object( $all_cached_themes ) ) { $all_cached_themes = (object) array( 'timestamp' => '', 'md5' => '', 'themes' => array(), ); } $time = time(); if ( ! empty( $all_cached_themes->timestamp ) && ( $time - $all_cached_themes->timestamp ) < WP_FS__TIME_5_MIN_IN_SEC ) { // Don't send theme updates if last update was in the past 5 min. return false; } // Write timestamp to lock the logic. $all_cached_themes->timestamp = $time; self::$_accounts->set_option( $option_name, $all_cached_themes, true ); // Reload options from DB. self::$_accounts->load( true ); $all_cached_themes = self::$_accounts->get_option( $option_name ); if ( $time != $all_cached_themes->timestamp ) { // If timestamp is different, then another thread captured the lock. return false; } // Get active theme. $active_theme = wp_get_theme(); $active_theme_stylesheet = $active_theme->get_stylesheet(); // Check if there's a change in themes. $all_themes = wp_get_themes(); // Check if themes changed. ksort( $all_themes ); $themes_signature = ''; foreach ( $all_themes as $slug => $data ) { $is_active = ( $slug === $active_theme_stylesheet ); $themes_signature .= $slug . ',' . $data->version . ',' . ( $is_active ? '1' : '0' ) . ';'; } // Check if themes status changed (version or active/inactive). $themes_changed = ( $all_cached_themes->md5 !== md5( $themes_signature ) ); $themes_update_data = array(); if ( $themes_changed ) { // Change in themes, report changes. // Update existing themes info. foreach ( $all_cached_themes->themes as $slug => $data ) { $is_active = ( $slug === $active_theme_stylesheet ); if ( ! isset( $all_themes[ $slug ] ) ) { // Plugin uninstalled. $uninstalled_theme_data = $data; $uninstalled_theme_data['is_active'] = false; $uninstalled_theme_data['is_uninstalled'] = true; $themes_update_data[] = $uninstalled_theme_data; unset( $all_themes[ $slug ] ); unset( $all_cached_themes->themes[ $slug ] ); } else if ( $data['is_active'] !== $is_active || $data['version'] !== $all_themes[ $slug ]->version ) { // Plugin activated or deactivated, or version changed. $all_cached_themes->themes[ $slug ]['is_active'] = $is_active; $all_cached_themes->themes[ $slug ]['version'] = $all_themes[ $slug ]->version; $themes_update_data[] = $all_cached_themes->themes[ $slug ]; } } // Find new themes that weren't yet seen before. foreach ( $all_themes as $slug => $data ) { if ( ! isset( $all_cached_themes->themes[ $slug ] ) ) { $is_active = ( $slug === $active_theme_stylesheet ); // New plugin. $new_plugin = array( 'slug' => $slug, 'version' => $data->version, 'title' => $data->name, 'is_active' => $is_active, 'is_uninstalled' => false, ); $themes_update_data[] = $new_plugin; $all_cached_themes->themes[ $slug ] = $new_plugin; } } $all_cached_themes->md5 = md5( $themes_signature ); $all_cached_themes->timestamp = time(); self::$_accounts->set_option( $option_name, $all_cached_themes, true ); } return $themes_update_data; } /** * Get site data for API install request. * * @author Vova Feldman (@svovaf) * @since 1.1.2 * * @param string[] $override * @param bool $include_plugins Since 1.1.8 by default include plugin changes. * @param bool $include_themes Since 1.1.8 by default include plugin changes. * @param bool $include_blog_data Since 2.3.0 by default include the current blog's data (language, title, and URL). * * @return array */ private function get_install_data_for_api( array $override, $include_plugins = true, $include_themes = true, $include_blog_data = true ) { // Alias. $permissions = FS_Permission_Manager::instance( $this ); if ( $permissions->is_extensions_tracking_allowed() ) { if ( ! defined( 'WP_FS__TRACK_PLUGINS' ) || false !== WP_FS__TRACK_PLUGINS ) { /** * @since 1.1.8 Also send plugin updates. */ if ( $include_plugins && ! isset( $override['plugins'] ) ) { $plugins = $this->get_plugins_data_for_api(); if ( ! empty( $plugins ) ) { $override['plugins'] = $plugins; } } } if ( ! defined( 'WP_FS__TRACK_THEMES' ) || false !== WP_FS__TRACK_THEMES ) { /** * @since 1.1.8 Also send themes updates. */ if ( $include_themes && ! isset( $override['themes'] ) ) { $themes = $this->get_themes_data_for_api(); if ( ! empty( $themes ) ) { $override['themes'] = $themes; } } } } $versions = $this->get_versions(); $blog_data = array(); if ( $include_blog_data ) { $blog_data['url'] = self::get_unfiltered_site_url(); if ( $permissions->is_diagnostic_tracking_allowed() ) { $blog_data = array_merge( $blog_data, array( 'language' => self::get_sanitized_language(), 'title' => get_bloginfo( 'name' ), ) ); } } return array_merge( $versions, $blog_data, array( 'version' => $this->get_plugin_version(), 'is_premium' => $this->is_premium(), // Special params. 'is_active' => true, 'is_uninstalled' => false, ), $override ); } /** * Update installs details. * * @todo V1 of multiste network support doesn't support plugin and theme data sending. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string[] string $override * @param bool $only_diff * @param bool $is_keepalive * @param bool $include_plugins Since 1.1.8 by default include plugin changes. * @param bool $include_themes Since 1.1.8 by default include plugin changes. * * @return array */ private function get_installs_data_for_api( array $override, $only_diff = false, $is_keepalive = false, $include_plugins = true, $include_themes = true ) { /** * @since 1.1.8 Also send plugin updates. */ // if ( $include_plugins && ! isset( $override['plugins'] ) ) { // $plugins = $this->get_plugins_data_for_api(); // if ( ! empty( $plugins ) ) { // $override['plugins'] = $plugins; // } // } /** * @since 1.1.8 Also send themes updates. */ // if ( $include_themes && ! isset( $override['themes'] ) ) { // $themes = $this->get_themes_data_for_api(); // if ( ! empty( $themes ) ) { // $override['themes'] = $themes; // } // } // Common properties. $versions = $this->get_versions(); $common = array_merge( $versions, array( 'version' => $this->get_plugin_version(), 'is_premium' => $this->is_premium(), ), $override ); $is_common_diff_for_any_site = false; $common_diff_union = array(); $installs_data = array(); $sites = self::get_sites(); $subsite_data_for_api_by_install_id = array(); $install_url_by_install_id = array(); $subsite_registration_date_by_install_id = array(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $install = $this->get_install_by_blog_id( $blog_id ); if ( is_object( $install ) ) { if ( $install->user_id != $this->_user->id ) { // Install belongs to a different owner. continue; } if ( ! $this->is_tracking_allowed( $blog_id, $install ) ) { // Don't send updates regarding opted-out installs. continue; } $install_data = $this->get_site_info( $site, true ); if ( FS_Clone_Manager::instance()->is_temporary_duplicate_by_blog_id( $install_data['blog_id'] ) ) { continue; } $uid = $install_data['uid']; $url = $install_data['url']; $registration_date = $install_data['registration_date']; if ( isset( $subsite_data_for_api_by_install_id[ $install->id ] ) ) { $clone_subsite_data = $subsite_data_for_api_by_install_id[ $install->id ]; $clone_install_url = $install_url_by_install_id[ $install->id ]; $clone_subsite_registration_date = $subsite_registration_date_by_install_id[ $install->id ]; $skip = false; if ( ! empty( $install_data['registration_date'] ) && ! empty( $clone_subsite_registration_date ) ) { /** * If the current subsite was created after the other subsite that is also linked to the same install ID, we assume that it's a clone (not the original), and therefore, would skip its processing. * * @author Leo Fajardo (@leorw) * @since 2.5.1 */ $skip = ( strtotime( $install_data['registration_date'] ) > strtotime( $clone_subsite_registration_date ) ); } else if ( /** * If we already have an install with the same URL as the subsite it's stored in, skip the current subsite. Otherwise, replace the existing install's data with the current subsite's install's data if the URLs match. * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ fs_strip_url_protocol( untrailingslashit( $clone_install_url ) ) === fs_strip_url_protocol( untrailingslashit( $clone_subsite_data['url'] ) ) || fs_strip_url_protocol( untrailingslashit( $install->url ) ) !== fs_strip_url_protocol( untrailingslashit( $url ) ) ) { $skip = true; } if ( $skip ) { // Store the skipped subsite's ID so that the clone resolution manager can try to resolve the clone install that is stored in that subsite later on. FS_Clone_Manager::instance()->store_blog_install_info( $blog_id ); continue; } } unset( $install_data['blog_id'] ); unset( $install_data['uid'] ); unset( $install_data['url'] ); unset( $install_data['registration_date'] ); $install_data['is_active'] = $this->is_active_for_site( $blog_id ); $install_data['is_uninstalled'] = $install->is_uninstalled; $common_diff = null; $is_common_diff = false; if ( $only_diff ) { $install_data = $this->get_install_diff_for_api( $install_data, $install, $override ); $common_diff = $this->get_install_diff_for_api( $common, $install, $override ); $is_common_diff = ! empty( $common_diff ); if ( $is_common_diff ) { foreach ( $common_diff as $k => $v ) { if ( ! isset( $common_diff_union[ $k ] ) ) { $common_diff_union[ $k ] = $v; } } } $is_common_diff_for_any_site = $is_common_diff_for_any_site || $is_common_diff; } if ( ! empty( $install_data ) || $is_common_diff || $is_keepalive ) { // Add install ID and site unique ID. $install_data['id'] = $install->id; $install_data['uid'] = $uid; $install_data['url'] = $url; $subsite_data_for_api_by_install_id[ $install->id ] = $install_data; $install_url_by_install_id[ $install->id ] = $install->url; $subsite_registration_date_by_install_id[ $install->id ] = $registration_date; } } } restore_current_blog(); $installs_data = array_merge( $installs_data, array_values( $subsite_data_for_api_by_install_id ) ); if ( 0 < count( $installs_data ) && ( $is_common_diff_for_any_site || ! $only_diff ) ) { if ( ! $only_diff ) { $installs_data[] = $common; } else if ( ! empty( $common_diff_union ) ) { $installs_data[] = $common_diff_union; } } foreach ( $installs_data as &$data ) { $data = (object) $data; } return $installs_data; } /** * Compare site actual data to the stored install data and return the differences for an API data sync. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param array $site * @param FS_Site $install * @param string[] string $override * * @return array */ private function get_install_diff_for_api( $site, $install, $override = array() ) { $diff = array(); $special = array(); $special_override = false; foreach ( $site as $p => $v ) { if ( property_exists( $install, $p ) ) { if ( ( is_bool( $install->{$p} ) || ! empty( $install->{$p} ) ) && $install->{$p} != $v ) { $val = self::get_api_sanitized_property( $p, $v ); if ( $install->{$p} != $val ) { $install->{$p} = $val; $diff[ $p ] = $val; } } } else { $special[ $p ] = $v; if ( isset( $override[ $p ] ) || 'plugins' === $p || 'themes' === $p ) { $special_override = true; } } } if ( $special_override || 0 < count( $diff ) ) { // Add special params only if has at least one // standard param, or if explicitly requested to // override a special param or a param which is not exist // in the install object. $diff = array_merge( $diff, $special ); } return $diff; } /** * @author Leo Fajardo (@leorw) * @since 2.5.1 */ private function send_pending_clone_update_once() { $this->_logger->entrance(); if ( ! empty( $this->_storage->clone_id ) ) { return; } $install_clone = $this->get_api_site_scope()->call( '/clones', 'post', array( 'site_url' => self::get_unfiltered_site_url() ) ); if ( $this->is_api_result_entity( $install_clone ) ) { $this->_storage->clone_id = $install_clone->id; } } /** * @author Leo Fajardo (@leorw) * @since 2.5.1 * * @param string $resolution_type * @param FS_Site $clone_context_install */ function send_clone_resolution_update( $resolution_type, $clone_context_install ) { $this->_logger->entrance(); if ( empty( $this->_storage->clone_id ) ) { return; } $new_install_id = null; $current_site = null; $flush = false; /** * If the current site is now different from the context install before the clone resolution, we need to override `$this->_site` so that the API call below will be made with the right install scope entity. */ if ( $clone_context_install->id != $this->_site->id ) { $new_install_id = $this->_site->id; $current_site = $this->_site; $this->_site = $clone_context_install; $flush = true; } $this->get_api_site_scope( $flush )->call( "/clones/{$this->_storage->clone_id}", 'put', array( 'resolution' => $resolution_type, 'new_install_id' => $new_install_id, ) ); if ( is_object( $current_site ) ) { /** * Ensure that the install scope entity is updated back to the previous install entity. */ $this->_site = $current_site; // Restore the previous install scope entity of the API. $this->get_api_site_scope( true ); } } /** * Update install only if changed. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string[] string $override * @param bool $flush * @param bool $is_two_way_sync @since 2.5.0 If true and there's a successful API request, the install sync cron will be cleared. * * @return false|object|string */ private function send_install_update( $override = array(), $flush = false, $is_two_way_sync = false ) { $this->_logger->entrance(); $check_properties = $this->get_install_data_for_api( $override ); if ( $flush ) { $params = $check_properties; } else { $params = $this->get_install_diff_for_api( $check_properties, $this->_site, $override ); } if ( empty( $params ) ) { $keepalive_only_update = $this->should_send_keepalive_update(); if ( ! $keepalive_only_update ) { /** * There are no updates to send including keepalive. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ return false; } } if ( $is_two_way_sync ) { /** * Update last install sync timestamp during a two-way sync call as we expect that updates are sent during this call. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ if ( ! is_multisite() ) { // Update last install sync timestamp. $this->set_cron_execution_timestamp( 'install_sync' ); } $params['uid'] = $this->get_anonymous_id(); } $this->set_keepalive_timestamp(); // Send updated values to FS. $site = $this->api_site_call( '/', 'put', $params, true ); if ( $is_two_way_sync && $this->is_api_result_entity( $site ) ) { /** * Clear scheduled install sync after a two-way sync call. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ if ( ! is_multisite() ) { // I successfully sent install update, clear scheduled sync if exist. $this->clear_install_sync_cron(); } } return $site; } /** * Update installs only if changed. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param string[] string $override * @param bool $flush * @param bool $is_two_way_sync @since 2.5.0 If true and there's a successful API request, the install sync cron will be cleared. * * @return false|object|string */ private function send_installs_update( $override = array(), $flush = false, $is_two_way_sync = false ) { $this->_logger->entrance(); /** * Pass `true` to use the network level storage since the update is for many installs. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ $should_send_keepalive = $this->should_send_keepalive_update( true ); $installs_data = $this->get_installs_data_for_api( $override, ! $flush, $should_send_keepalive ); if ( empty( $installs_data ) ) { return false; } if ( $is_two_way_sync ) { // Update last install sync timestamp during a two-way sync call as we expect that updates are sent during this call. $this->set_cron_execution_timestamp( 'install_sync' ); } /** * Pass `true` to use the network level storage since the update is for many installs. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ $this->set_keepalive_timestamp( true ); // Send updated values to FS. $result = $this->get_api_user_scope()->call( "/plugins/{$this->_plugin->id}/installs.json", 'put', $installs_data ); if ( $is_two_way_sync && $this->is_api_result_object( $result, 'installs' ) ) { // I successfully sent a two-way installs update, clear the scheduled install sync if it exists. $this->clear_install_sync_cron(); } return $result; } /** * @author Leo Fajardo (@leorw) * * @param bool|null $use_network_level_storage * * @return bool */ private function should_send_keepalive_update( $use_network_level_storage = null ) { $keepalive_timestamp = $this->_storage->get( 'keepalive_timestamp', 0, $use_network_level_storage ); if ( $keepalive_timestamp < ( time() - WP_FS__TIME_WEEK_IN_SEC ) ) { // If updated more than 7 days ago, trigger a keepalive and update the time it was triggered. return true; } else { // If updated 7 days ago or less, "flip a coin", if the value is 7 trigger a keepalive and update the last time it was triggered. return ( 7 == rand( 1, 7 ) ); } } /** * Syncs the install owner's data if needed (i.e., if the install owner is different from the loaded user). * * @author Leo Fajardo (@leorw) * @since 2.3.2 */ private function maybe_sync_install_user() { if ( $this->_user->id == $this->_site->user_id ) { return; } // Fetch user data and store if found. $this->sync_user_by_current_install(); } /** * Update install only if changed. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string[] string $override * @param bool $flush */ function sync_install( $override = array(), $flush = false ) { $this->_logger->entrance(); $site = $this->send_install_update( $override, $flush, true ); if ( false === $site ) { // No sync required. return; } if ( ! $this->is_api_result_entity( $site ) ) { // Failed to sync, don't update locally. return; } $this->_site = new FS_Site( $site ); $this->_store_site( true ); } /** * Update install only if changed. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string[] string $override * @param bool $flush */ private function sync_installs( $override = array(), $flush = false ) { $this->_logger->entrance(); $result = $this->send_installs_update( $override, $flush, true ); if ( false === $result ) { // No sync required. return; } if ( ! $this->is_api_result_object( $result, 'installs' ) ) { // Failed to sync, don't update locally. return; } $address_to_blog_map = $this->get_address_to_blog_map(); foreach ( $result->installs as $install ) { $this->_site = new FS_Site( $install ); $address = trailingslashit( fs_strip_url_protocol( $install->url ) ); $blog_id = $address_to_blog_map[ $address ]; $this->_store_site( true, $blog_id ); } } /** * Track install's custom event. * * IMPORTANT: * Custom event tracking is currently only supported for specific clients. * If you are not one of them, please don't use this method. If you will, * the API will simply ignore your request based on the plugin ID. * * Need custom tracking for your plugin or theme? * If you are interested in custom event tracking please contact yo@freemius.com * for further details. * * @author Vova Feldman (@svovaf) * @since 1.2.1 * * @param string $name Event name. * @param array $properties Associative key/value array with primitive values only * @param bool $process_at A valid future date-time in the following format Y-m-d H:i:s. * @param bool $once If true, event will be tracked only once. IMPORTANT: Still trigger the API call. * * @return object|false Event data or FALSE on failure. * * @throws \Freemius_InvalidArgumentException */ public function track_event( $name, $properties = array(), $process_at = false, $once = false ) { $this->_logger->entrance( http_build_query( array( 'name' => $name, 'once' => $once ) ) ); if ( ! $this->is_registered() ) { return false; } $event = array( 'type' => $name ); if ( is_numeric( $process_at ) && $process_at > time() ) { $event['process_at'] = $process_at; } if ( $once ) { $event['once'] = true; } if ( ! empty( $properties ) ) { // Verify associative array values are primitive. foreach ( $properties as $k => $v ) { if ( ! is_scalar( $v ) ) { throw new Freemius_InvalidArgumentException( 'The $properties argument must be an associative key/value array with primitive values only.' ); } } $event['properties'] = $properties; } $result = $this->get_api_site_scope()->call( 'events.json', 'post', $event ); return $this->is_api_error( $result ) ? false : $result; } /** * Track install's custom event only once, but it still triggers the API call. * * IMPORTANT: * Custom event tracking is currently only supported for specific clients. * If you are not one of them, please don't use this method. If you will, * the API will simply ignore your request based on the plugin ID. * * Need custom tracking for your plugin or theme? * If you are interested in custom event tracking please contact yo@freemius.com * for further details. * * @author Vova Feldman (@svovaf) * @since 1.2.1 * * @param string $name Event name. * @param array $properties Associative key/value array with primitive values only * @param bool $process_at A valid future date-time in the following format Y-m-d H:i:s. * * @return object|false Event data or FALSE on failure. * * @throws \Freemius_InvalidArgumentException * * @user Freemius::track_event() */ public function track_event_once( $name, $properties = array(), $process_at = false ) { return $this->track_event( $name, $properties, $process_at, true ); } /** * Plugin uninstall hook. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param bool $check_user Enforce checking if user have plugins activation privileges. */ function _uninstall_plugin_event( $check_user = true ) { $this->_logger->entrance( 'slug = ' . $this->_slug ); if ( $check_user && ! current_user_can( 'activate_plugins' ) ) { return; } $params = array(); $uninstall_reason = null; if ( isset( $this->_storage->uninstall_reason ) ) { $uninstall_reason = $this->_storage->uninstall_reason; $params['reason_id'] = $uninstall_reason->id; $params['reason_info'] = $uninstall_reason->info; } if ( ! $this->is_registered() ) { // Send anonymous uninstall event only if user submitted a feedback. if ( isset( $uninstall_reason ) ) { if ( isset( $uninstall_reason->is_anonymous ) && ! $uninstall_reason->is_anonymous ) { $this->opt_in( false, false, false, false, true ); } else { $params['uid'] = $this->get_anonymous_id(); $this->get_api_plugin_scope()->call( 'uninstall.json', 'put', $params ); } } } else { $params = array_merge( $params, array( 'is_active' => false, 'is_uninstalled' => true, ) ); if ( $this->_is_network_active ) { // Send uninstall event. $this->send_installs_update( $params ); } else { // Send uninstall event and handle the result. $this->sync_install( $params ); } } // @todo Decide if we want to delete plugin information from db. } /** * Set the basename of the current product and hook _activate_plugin_event_hook() to the activation action. * * @author Vova Feldman (@svovaf) * @since 2.2.1 * * @param string $is_premium * @param string $caller * * @return void */ function set_basename( $is_premium, $caller ) { $basename = plugin_basename( $caller ); $current_basename = $is_premium ? $this->_premium_plugin_basename : $this->_free_plugin_basename; if ( $current_basename == $basename ) { // Basename value set correctly. return; } if ( $is_premium ) { $this->_premium_plugin_basename = $basename; } else { $this->_free_plugin_basename = $basename; } $plugin_dir = dirname( $this->_plugin_dir_path ) . '/'; register_activation_hook( $plugin_dir . $basename, array( &$this, '_activate_plugin_event_hook' ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.1 * @since 2.2.1 If the context product is in its premium version, use the current module's basename, even if it was renamed. * * @return string */ function premium_plugin_basename() { if ( ! isset( $this->_premium_plugin_basename ) ) { $this->_premium_plugin_basename = $this->is_premium() ? // The product is premium, so use the current basename. $this->_plugin_basename : $this->get_premium_slug() . '/' . basename( $this->_free_plugin_basename ); } return $this->_premium_plugin_basename; } /** * Uninstall plugin hook. Called only when connected his account with Freemius for active sites tracking. * * @author Vova Feldman (@svovaf) * @since 1.0.2 */ public static function _uninstall_plugin_hook() { self::_load_required_static(); self::$_static_logger->entrance(); if ( ! current_user_can( 'activate_plugins' ) ) { return; } $plugin_file = substr( current_filter(), strlen( 'uninstall_' ) ); self::$_static_logger->info( 'plugin = ' . $plugin_file ); define( 'WP_FS__UNINSTALL_MODE', true ); $fs = self::get_instance_by_file( $plugin_file ); if ( is_object( $fs ) ) { $fs->remove_sdk_reference(); self::require_plugin_essentials(); if ( is_plugin_active( $fs->_free_plugin_basename ) || is_plugin_active( $fs->premium_plugin_basename() ) ) { // Deleting Free or Premium plugin version while the other version still installed. return; } if ( ! $fs->is_clone() && /** * If there's a context install, run this method only when there's also a context user (e.g., when cloning a subsite of a multisite network into a single-site installation, it's possible for an install to be associated with a non-existing user entity; we want Freemius to be off in this case, while we are trying to recover the user). * * @author Leo Fajardo */ ( ! is_object( $fs->_site ) || $fs->is_registered() ) ) { $fs->_uninstall_plugin_event(); } $fs->do_action( 'after_uninstall' ); } } #---------------------------------------------------------------------------------- #region Plugin Information #---------------------------------------------------------------------------------- /** * Load WordPress core plugin.php essential module. * * @author Vova Feldman (@svovaf) * @since 1.1.1 */ private static function require_plugin_essentials() { if ( ! function_exists( 'get_plugins' ) ) { self::$_static_logger->log( 'Including wp-admin/includes/plugin.php...' ); require_once ABSPATH . 'wp-admin/includes/plugin.php'; } } /** * Load WordPress core pluggable.php module. * * @author Vova Feldman (@svovaf) * @since 1.1.2 */ private static function require_pluggable_essentials() { if ( ! function_exists( 'wp_get_current_user' ) ) { require_once ABSPATH . 'wp-includes/pluggable.php'; } } /** * Return plugin data. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param bool $reparse_plugin_metadata * * @return array */ function get_plugin_data( $reparse_plugin_metadata = false ) { if ( ! isset( $this->_plugin_data ) || $reparse_plugin_metadata ) { self::require_plugin_essentials(); if ( $this->is_plugin() ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.0 When using get_plugin_data() do NOT translate plugin data. * * @link https://github.com/Freemius/wordpress-sdk/issues/77 */ $plugin_data = get_plugin_data( $this->_plugin_main_file_path, false, false ); } else { $theme_data = wp_get_theme(); if ( $this->_plugin_basename !== $theme_data->get_stylesheet() && is_child_theme() ) { $parent_theme = $theme_data->parent(); if ( ( $parent_theme instanceof WP_Theme ) && $this->_plugin_basename === $parent_theme->get_stylesheet() ) { $theme_data = $parent_theme; } } $plugin_data = array( 'Name' => $theme_data->get( 'Name' ), 'Version' => $theme_data->get( 'Version' ), 'Author' => $theme_data->get( 'Author' ), 'Description' => $theme_data->get( 'Description' ), 'PluginURI' => $theme_data->get( 'ThemeURI' ), ); } $this->_plugin_data = $plugin_data; } return $this->_plugin_data; } /** * @author Vova Feldman (@svovaf) * @since 1.0.1 * @since 1.2.2.5 If slug not set load slug by module ID. * * @return string Plugin slug. */ function get_slug() { if ( ! isset( $this->_slug ) ) { $id_slug_type_path_map = self::$_accounts->get_option( 'id_slug_type_path_map', array() ); $this->_slug = $id_slug_type_path_map[ $this->_module_id ]['slug']; } return $this->_slug; } /** * @author Leo Fajardo (@leorw) * @since 2.2.1 * * @return string */ function get_premium_slug() { return ( is_object( $this->_plugin ) && ! empty( $this->_plugin->premium_slug ) ) ? $this->_plugin->premium_slug : "{$this->_slug}-premium"; } /** * Retrieve the desired folder name for the product. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @return string Plugin slug. */ function get_target_folder_name() { return $this->can_use_premium_code() ? $this->_plugin->premium_slug : $this->_slug; } /** * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @return number Plugin ID. */ function get_id() { return $this->_plugin->id; } /** * @author Leo Fajardo (@leorw) * @since 2.2.4 * * @return number|null Bundle ID. */ function get_bundle_id() { return ( isset( $this->_plugin->bundle_id ) && FS_Plugin::is_valid_id( $this->_plugin->bundle_id ) ) ? $this->_plugin->bundle_id : null; } /** * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @return string|null Bundle public key. */ function get_bundle_public_key() { return isset( $this->_plugin->bundle_public_key ) ? $this->_plugin->bundle_public_key : null; } /** * Get whether the SDK has been initiated in the context of a Bundle. * * This will return true, if `bundle_id` is present in the SDK init parameters. * * ```php * $my_fs = fs_dynamic_init( array( * // ... * 'bundle_id' => 'XXXX', // Will return true since we have bundle id. * 'bundle_public_key' => 'pk_XXXX', * ) ); * ``` * * @author Swashata Ghosh (@swashata) * @since 2.5.0 * * @return bool True if we are running in bundle context, false otherwise. */ private function has_bundle_context() { return ! is_null( $this->get_bundle_id() ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @return string Freemius SDK version */ function get_sdk_version() { return $this->version; } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @return number Parent plugin ID (if parent exist). */ function get_parent_id() { return $this->is_addon() ? $this->get_parent_instance()->get_id() : $this->_plugin->id; } /** * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @return string */ function get_usage_tracking_terms_url() { return $this->apply_filters( 'usage_tracking_terms_url', "https://freemius.com/product/opt-in/{$this->_plugin->id}/{$this->_slug}/" ); } /** * @todo (For LiteSDK) We can refactor this and other related functions giving links to several landing pages on freemius.com to come from a separate class like `FS_Terms_Pages`. This would get a `FS_WP_Hook` (hypothetical) instance as a dependency and use it to hook into the `license_activation_terms_url` or related filters. The entry level instance from `ms_fs()` would hold a public read-only variable `my_fs()->terms_pages` which would be an instance of `FS_Terms_Pages` and would hold all the links to the terms pages. * @since 2.5.8 * * @return string */ function get_license_activation_terms_url() { return $this->apply_filters( 'license_activation_terms_url', "https://freemius.com/product/license-activation/{$this->_plugin->id}/{$this->_slug}/" ); } /** * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @return string */ function get_eula_url() { return $this->apply_filters( 'eula_url', "https://freemius.com/product/{$this->_plugin->id}/{$this->_slug}/legal/eula/" ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @return string Plugin public key. */ function get_public_key() { return $this->_plugin->public_key; } /** * Will be available only on sandbox mode. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @return mixed Plugin secret key. */ function get_secret_key() { return $this->_plugin->secret_key; } /** * @author Vova Feldman (@svovaf) * @since 1.1.1 * * @return bool */ function has_secret_key() { return ! empty( $this->_plugin->secret_key ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string|bool $premium_suffix * * @return string */ function get_plugin_name( $premium_suffix = false ) { $this->_logger->entrance(); /** * This `if-else` can be squeezed into a single `if` but I intentionally split it for code readability. * * @author Vova Feldman */ if ( ! isset( $this->_plugin_name ) ) { // Name is not yet set. $this->set_name( $premium_suffix ); } else if ( ! empty( $premium_suffix ) && ( ! is_object( $this->_plugin ) || $this->_plugin->premium_suffix !== $premium_suffix ) ) { // Name is already set, but there's a change in the premium suffix. $this->set_name( $premium_suffix ); } return $this->_plugin_name; } /** * Calculates and stores the product's name. This helper function was created specifically for get_plugin_name() just to make the code clearer. * * @author Vova Feldman (@svovaf) * @since 2.2.1 * * @param string $premium_suffix */ private function set_name( $premium_suffix = '' ) { $plugin_data = $this->get_plugin_data(); // Get name. $this->_plugin_name = $plugin_data['Name']; if ( is_string( $premium_suffix ) ) { $premium_suffix = trim( $premium_suffix ); if ( ! empty( $premium_suffix ) ) { // Check if plugin name contains " (premium)" or a custom suffix and remove it. $suffix = ( ' ' . strtolower( $premium_suffix ) ); $suffix_len = strlen( $suffix ); if ( strlen( $plugin_data['Name'] ) > $suffix_len && $suffix === substr( strtolower( $plugin_data['Name'] ), - $suffix_len ) ) { $this->_plugin_name = substr( $plugin_data['Name'], 0, - $suffix_len ); } } } $this->_logger->departure( 'Name = ' . $this->_plugin_name ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.0 * * @param bool $reparse_plugin_metadata * * @return string */ function get_plugin_version( $reparse_plugin_metadata = false ) { $this->_logger->entrance(); $plugin_data = $this->get_plugin_data( $reparse_plugin_metadata ); $this->_logger->departure( 'Version = ' . $plugin_data['Version'] ); return $this->apply_filters( 'plugin_version', $plugin_data['Version'] ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @return string */ function get_plugin_title() { $this->_logger->entrance(); $title = $this->_plugin->title; return $this->apply_filters( 'plugin_title', $title ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @param bool $lowercase * * @return string */ function get_module_label( $lowercase = false ) { $label = $this->is_addon() ? $this->get_text_inline( 'Add-On', 'addon' ) : ( $this->is_plugin() ? $this->get_text_inline( 'Plugin', 'plugin' ) : $this->get_text_inline( 'Theme', 'theme' ) ); if ( $lowercase ) { $label = strtolower( $label ); } return $label; } /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @return string */ function get_plugin_basename() { if ( ! isset( $this->_plugin_basename ) ) { if ( $this->is_plugin() ) { $this->_plugin_basename = plugin_basename( $this->_plugin_main_file_path ); } else { $this->_plugin_basename = basename( dirname( $this->_plugin_main_file_path ) ); } } return $this->_plugin_basename; } function get_plugin_folder_name() { $this->_logger->entrance(); $plugin_folder = $this->_plugin_basename; while ( '.' !== dirname( $plugin_folder ) ) { $plugin_folder = dirname( $plugin_folder ); } $this->_logger->departure( 'Folder Name = ' . $plugin_folder ); return $plugin_folder; } #endregion ------------------------------------------------------------------ /* Account ------------------------------------------------------------------------------------------------------------------*/ /** * Find plugin's slug by plugin's basename. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string $plugin_base_name * * @return false|string */ private static function find_slug_by_basename( $plugin_base_name ) { $file_slug_map = self::$_accounts->get_option( 'file_slug_map', array() ); if ( ! array( $file_slug_map ) || ! isset( $file_slug_map[ $plugin_base_name ] ) ) { return false; } return $file_slug_map[ $plugin_base_name ]; } /** * Store the map between the plugin's basename to the slug. * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ private function store_file_slug_map() { $file_slug_map = self::$_accounts->get_option( 'file_slug_map', array() ); if ( ! array( $file_slug_map ) ) { $file_slug_map = array(); } if ( ! isset( $file_slug_map[ $this->_plugin_basename ] ) || $file_slug_map[ $this->_plugin_basename ] !== $this->_slug ) { $file_slug_map[ $this->_plugin_basename ] = $this->_slug; self::$_accounts->set_option( 'file_slug_map', $file_slug_map, true ); } } /** * @return array[number]FS_User */ static function get_all_users() { $users = self::maybe_get_entities_account_option( 'users', array() ); if ( ! is_array( $users ) ) { $users = array(); } return $users; } /** * @param string $module_type * @param null|int $blog_id Since 2.0.0 * * @return array[string]FS_Site */ public static function get_all_sites( $module_type = WP_FS__MODULE_TYPE_PLUGIN, $blog_id = null, $is_backup = false ) { $sites = self::get_account_option( ( $is_backup ? 'prev_' : '' ) . 'sites', $module_type, $blog_id ); if ( ! is_array( $sites ) ) { $sites = array(); } return $sites; } /** * @author Leo Fajardo (@leorw) * * @since 1.2.2 * * @param string $option_name * @param string $module_type * @param null|int $network_level_or_blog_id Since 2.0.0 * * @return mixed */ public static function get_account_option( $option_name, $module_type = null, $network_level_or_blog_id = null ) { if ( ! is_null( $module_type ) && WP_FS__MODULE_TYPE_PLUGIN !== $module_type ) { $option_name = $module_type . '_' . $option_name; } return self::maybe_get_entities_account_option( $option_name, array(), $network_level_or_blog_id ); } /** * @author Leo Fajardo (@leorw) * * @since 1.2.2 * * @param string $option_name * @param mixed $option_value * @param bool $store * @param null|int $network_level_or_blog_id Since 2.0.0 */ private function set_account_option( $option_name, $option_value, $store, $network_level_or_blog_id = null ) { self::set_account_option_by_module( $this->_module_type, $option_name, $option_value, $store, $network_level_or_blog_id ); } /** * @author Vova Feldman (@svovaf) * * @since 1.2.2.7 * * @param string $module_type * @param string $option_name * @param mixed $option_value * @param bool $store * @param null|int $network_level_or_blog_id Since 2.0.0 */ private static function set_account_option_by_module( $module_type, $option_name, $option_value, $store, $network_level_or_blog_id = null ) { if ( WP_FS__MODULE_TYPE_PLUGIN != $module_type ) { $option_name = $module_type . '_' . $option_name; } self::$_accounts->set_option( $option_name, $option_value, $store, $network_level_or_blog_id ); } /** * This method can also return non-entity or non-entities collection option like the `user_id_license_ids_map` option. * * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @param string $option_name * @param mixed $default * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS). * * @return mixed|FS_Plugin[]|FS_User[]|FS_Site[]|FS_Plugin_License[]|FS_Plugin_Plan[]|FS_Plugin_Tag[] */ private static function maybe_get_entities_account_option( $option_name, $default = null, $network_level_or_blog_id = null ) { $option = self::$_accounts->get_option( $option_name, $default, $network_level_or_blog_id ); $class_name = ''; if ( fs_starts_with( $option_name, WP_FS__MODULE_TYPE_THEME . '_' ) ) { $option_name = str_replace( WP_FS__MODULE_TYPE_THEME . '_', '', $option_name ); } switch ( $option_name ) { case 'plugins': case 'themes': case 'addons': $class_name = FS_Plugin::get_class_name(); break; case 'users': $class_name = FS_User::get_class_name(); break; case 'sites': $class_name = FS_Site::get_class_name(); break; case 'licenses': case 'all_licenses': $class_name = FS_Plugin_License::get_class_name(); break; case 'plans': $class_name = FS_Plugin_Plan::get_class_name(); break; case 'updates': $class_name = FS_Plugin_Tag::get_class_name(); break; } if ( empty( $class_name ) ) { return $option; } return fs_get_entities( $option, $class_name ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param number|null $module_id * * @return FS_Plugin_License[] */ private static function get_all_licenses( $module_id = null ) { $licenses = self::get_account_option( 'all_licenses' ); if ( ! is_array( $licenses ) ) { $licenses = array(); } if ( is_null( $module_id ) ) { return $licenses; } $licenses = isset( $licenses[ $module_id ] ) ? $licenses[ $module_id ] : array(); return $licenses; } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param number $module_id * @param number|null $user_id * * @return array */ private static function get_user_id_license_ids_map( $module_id, $user_id = null ) { $all_modules_user_id_license_ids_map = self::get_account_option( 'user_id_license_ids_map' ); if ( ! is_array( $all_modules_user_id_license_ids_map ) ) { $all_modules_user_id_license_ids_map = array(); } $user_id_license_ids_map = isset( $all_modules_user_id_license_ids_map[ $module_id ] ) ? $all_modules_user_id_license_ids_map[ $module_id ] : array(); if ( FS_User::is_valid_id( $user_id ) ) { $user_id_license_ids_map = isset( $user_id_license_ids_map[ $user_id ] ) ? $user_id_license_ids_map[ $user_id ] : array(); } return $user_id_license_ids_map; } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param array $new_user_id_license_ids_map * @param number $module_id * @param number|null $user_id */ private static function store_user_id_license_ids_map( $new_user_id_license_ids_map, $module_id, $user_id = null ) { $all_modules_user_id_license_ids_map = self::get_account_option( 'user_id_license_ids_map' ); if ( ! is_array( $all_modules_user_id_license_ids_map ) ) { $all_modules_user_id_license_ids_map = array(); } if ( ! isset( $all_modules_user_id_license_ids_map[ $module_id ] ) ) { $all_modules_user_id_license_ids_map[ $module_id ] = array(); } if ( FS_User::is_valid_id( $user_id ) ) { $all_modules_user_id_license_ids_map[ $module_id ][ $user_id ] = $new_user_id_license_ids_map; } else { $all_modules_user_id_license_ids_map[ $module_id ] = $new_user_id_license_ids_map; } self::$_accounts->set_option( 'user_id_license_ids_map', $all_modules_user_id_license_ids_map, true ); } /** * Get a collection of the user's linked license IDs. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $user_id * * @return number[] */ private function get_user_linked_license_ids( $user_id ) { return self::get_user_id_license_ids_map( $this->_module_id, $user_id ); } /** * Override the user's linked license IDs with a new IDs collection. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $user_id * @param number[] $license_ids */ private function set_user_linked_license_ids( $user_id, array $license_ids ) { self::store_user_id_license_ids_map( $license_ids, $this->_module_id, $user_id ); } /** * Link a specified license ID to a given user. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $license_id * @param number $user_id */ private function link_license_2_user( $license_id, $user_id ) { $license_ids = $this->get_user_linked_license_ids( $user_id ); if ( in_array( $license_id, $license_ids ) ) { // License already linked. return; } $license_ids[] = $license_id; $this->set_user_linked_license_ids( $user_id, $license_ids ); } /** * @param string|bool $module_type * * @return FS_Plugin_Plan[] */ private static function get_all_plans( $module_type = false ) { $plans = self::get_account_option( 'plans', $module_type ); if ( ! is_array( $plans ) ) { $plans = array(); } return $plans; } /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @return FS_Plugin_Tag[] */ private static function get_all_updates() { $updates = self::maybe_get_entities_account_option( 'updates', array() ); if ( ! is_array( $updates ) ) { $updates = array(); } return $updates; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return array|false */ public static function get_all_addons() { $addons = self::maybe_get_entities_account_option( 'addons', array() ); if ( ! is_array( $addons ) ) { $addons = array(); } return $addons; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return number[]|false */ public static function get_all_account_addons() { $addons = self::$_accounts->get_option( 'account_addons', array() ); if ( ! is_array( $addons ) ) { $addons = array(); } return $addons; } /** * Check if user has connected his account (opted-in). * * Note: * If the user opted-in and opted-out on a later stage, * this will still return true. If you want to check if the * user is currently opted-in, use: * `$fs->is_registered() && $fs->is_tracking_allowed()` * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param bool $ignore_anonymous_state Since 2.5.1 * * @return bool */ function is_registered( $ignore_anonymous_state = false ) { return ( is_object( $this->_user ) && ( $this->is_premium() || $ignore_anonymous_state || ! $this->is_anonymous() ) ); } /** * Returns TRUE if the user opted-in and didn't disconnect (opt-out). * * @author Leo Fajardo (@leorw) * @since 1.2.1.5 * * @return bool */ function is_tracking_allowed( $blog_id = null, $install = null ) { if ( is_null( $install ) ) { $install = is_null( $blog_id ) ? $this->_site : $this->get_install_by_blog_id( $blog_id ); } return ( is_object( $install ) && FS_Permission_Manager::instance( $this )->is_homepage_url_tracking_allowed( $blog_id ) ); } /** * Returns TRUE if the user never opted-in or manually opted-out. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param int|null $blog_id * * @return bool */ function is_tracking_prohibited( $blog_id = null ) { return ( ! $this->is_registered( true ) || ! $this->is_tracking_allowed( $blog_id ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.4.0 * * @return bool */ function is_bundle_license_auto_activation_enabled() { return $this->is_addon() ? ( is_object( $this->_parent ) && $this->_parent->is_bundle_license_auto_activation_enabled() ) : $this->_is_bundle_license_auto_activation_enabled; } /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @return FS_Plugin */ function get_plugin() { return $this->_plugin; } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @return FS_User */ function get_user() { return $this->_user; } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @return FS_Site */ function get_site() { return $this->_site; } /** * @author Daniele Alessandra (@danielealessandra) * @return FS_Storage * @since 2.6.2 * */ public function get_storage() { return $this->_storage; } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function store_site( $site ) { $this->_site = $site; $this->_store_site( true ); } /** * Deletes the current install with an option to back it up in case restoration will be needed (e.g., if the automatic clone resolution attempt fails). * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function delete_current_install( $back_up ) { // Back up and delete the unique ID. if ( $back_up ) { self::$_accounts->set_option( 'prev_unique_id', $this->get_anonymous_id() ); } self::$_accounts->set_option( 'unique_id', null ); if ( $back_up ) { // Back up the install before deleting it so that it can be restored later on if necessary (e.g., if the automatic clone resolution attempt fails). $this->back_up_site(); } $this->_delete_site(); $this->_site = null; } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function restore_backup_site() { self::$_accounts->set_option( 'unique_id', self::$_accounts->get_option( 'prev_unique_id' ) ); $sites = self::get_all_sites( $this->_module_type, null, true ); $this->store_site( clone $sites[ $this->_slug ] ); } /** * Get plugin add-ons. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @since 1.1.7.3 If not yet loaded, fetch data from the API. * * @param bool $flush * * @return FS_Plugin[]|false */ function get_addons( $flush = false ) { $this->_logger->entrance(); if ( ! $this->_has_addons ) { return false; } $addons = $this->sync_addons( $flush ); return ( ! is_array( $addons ) || empty( $addons ) ) ? false : $addons; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return number[]|false */ function get_account_addons() { $this->_logger->entrance(); $addons = self::get_all_account_addons(); if ( ! is_array( $addons ) || ! isset( $addons[ $this->_plugin->id ] ) || ! is_array( $addons[ $this->_plugin->id ] ) || 0 === count( $addons[ $this->_plugin->id ] ) ) { return false; } return $addons[ $this->_plugin->id ]; } /** * Check if user has any * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @return bool */ function has_account_addons() { $addons = $this->get_account_addons(); return is_array( $addons ) && ( 0 < count( $addons ) ); } /** * Get add-on by ID (from local data). * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param number $id * * @return FS_Plugin|false */ function get_addon( $id ) { $this->_logger->entrance(); $addons = $this->get_addons(); if ( is_array( $addons ) ) { foreach ( $addons as $addon ) { if ( $id == $addon->id ) { return $addon; } } } return false; } /** * Get add-on by slug (from local data). * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param string $slug * * @param bool $flush * * @return FS_Plugin|false */ function get_addon_by_slug( $slug, $flush = false ) { $this->_logger->entrance(); $addons = $this->get_addons( $flush ); if ( is_array( $addons ) ) { foreach ( $addons as $addon ) { if ( $slug === $addon->slug ) { return $addon; } } } return false; } /** * @var array { * @key number Add-on ID. * @val object[] The add-on's plans and prices object. * } */ private $plans_and_pricing_by_addon_id; /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @return array { * @key number Add-on ID. * @val object[] The add-on's plans and prices object. * } */ function _get_addons_plans_and_pricing_map_by_id() { if ( ! isset( $this->plans_and_pricing_by_addon_id ) ) { $result = $this->get_api_plugin_scope()->get( $this->add_show_pending( "/addons/pricing.json?type=visible" ) ); $plans_and_pricing_by_addon_id = array(); if ( $this->is_api_result_object( $result, 'addons' ) ) { foreach ( $result->addons as $addon ) { $plans_and_pricing_by_addon_id[ $addon->id ] = $addon->plans; } } $this->plans_and_pricing_by_addon_id = $plans_and_pricing_by_addon_id; } return $this->plans_and_pricing_by_addon_id; } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param number $addon_id * @param bool $is_installed * * @return array */ function _get_addon_info( $addon_id, $is_installed ) { $addon = $this->get_addon( $addon_id ); if ( ! is_object( $addon ) ) { // Unexpected call. return array(); } $slug = $addon->slug; $addon_storage = FS_Storage::instance( WP_FS__MODULE_TYPE_PLUGIN, $slug ); if ( ! fs_is_network_admin() ) { // Get blog-level activated installations. $sites = self::maybe_get_entities_account_option( 'sites', array() ); } else { $sites = null; if ( $this->is_addon_activated( $addon_id ) && $this->get_addon_instance( $addon_id )->is_network_active() ) { if ( FS_Site::is_valid_id( $addon_storage->network_install_blog_id ) ) { // Get network-level activated installations. $sites = self::maybe_get_entities_account_option( 'sites', array(), $addon_storage->network_install_blog_id ); } } } $addon_info = array( 'is_connected' => false, 'slug' => $slug, 'title' => $addon->title, 'is_whitelabeled' => $addon_storage->is_whitelabeled ); if ( ! $is_installed ) { $plans_and_pricing_by_addon_id = $this->_get_addons_plans_and_pricing_map_by_id(); if ( isset( $plans_and_pricing_by_addon_id[ $addon_id ] ) ) { $has_paid_plan = false; $plans = $plans_and_pricing_by_addon_id[ $addon_id ]; if ( is_array( $plans ) && count( $plans ) > 0 ) { foreach ( $plans as $plan ) { if ( isset( $plan->pricing ) && is_array( $plan->pricing ) && count( $plan->pricing ) > 0 ) { $has_paid_plan = true; break; } } } $addon_info['has_paid_plan'] = $has_paid_plan; } } if ( ! is_array( $sites ) || ! isset( $sites[ $slug ] ) ) { return $addon_info; } $site = $sites[ $slug ]; $addon_info['is_connected'] = ( ( $addon->parent_plugin_id == $this->get_id() ) && is_object( $site ) && FS_Site::is_valid_id( $site->id ) && FS_User::is_valid_id( $site->user_id ) && FS_Plugin_Plan::is_valid_id( $site->plan_id ) ); if ( $addon_info['is_connected'] && $is_installed ) { return $addon_info; } $addon_info['site'] = $site; $plugins_data = self::maybe_get_entities_account_option( WP_FS__MODULE_TYPE_PLUGIN . 's', array() ); if ( isset( $plugins_data[ $slug ] ) ) { $plugin_data = $plugins_data[ $slug ]; $addon_info['version'] = $plugin_data->version; } $all_plans = self::maybe_get_entities_account_option( 'plans', array() ); if ( isset( $all_plans[ $slug ] ) ) { $plans = $all_plans[ $slug ]; foreach ( $plans as $plan ) { if ( $site->plan_id == Freemius::_decrypt( $plan->id ) ) { $addon_info['plan_name'] = Freemius::_decrypt( $plan->name ); $addon_info['plan_title'] = Freemius::_decrypt( $plan->title ); break; } } } $licenses = self::maybe_get_entities_account_option( 'all_licenses', array() ); if ( is_array( $licenses ) && isset( $licenses[ $addon_id ] ) ) { foreach ( $licenses[ $addon_id ] as $license ) { if ( $license->id == $site->license_id ) { $addon_info['license'] = $license; break; } } } if ( isset( $addon_info['license'] ) ) { if ( isset( $addon_storage->subscriptions ) && ! empty( $addon_storage->subscriptions ) ) { $addon_subscriptions = fs_get_entities( $addon_storage->subscriptions, FS_Subscription::get_class_name() ); foreach ( $addon_subscriptions as $subscription ) { if ( $subscription->license_id == $site->license_id ) { $addon_info['subscription'] = $subscription; break; } } } } return $addon_info; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $user_id * * @return FS_User */ static function _get_user_by_id( $user_id ) { self::$_static_logger->entrance( "user_id = {$user_id}" ); $users = self::get_all_users(); if ( is_array( $users ) ) { if ( isset( $users[ $user_id ] ) && $users[ $user_id ] instanceof FS_User && $user_id == $users[ $user_id ]->id ) { return $users[ $user_id ]; } // If user wasn't found by the key, iterate over all the users collection. foreach ( $users as $user ) { /** * @var FS_User $user */ if ( $user_id == $user->id ) { return $user; } } } return null; } /** * Checks if a Freemius user_id is associated with a super-admin. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $user_id * * @return bool */ private static function is_super_admin( $user_id ) { $is_super_admin = false; $user = self::_get_user_by_id( $user_id ); if ( $user instanceof FS_User && ! empty( $user->email ) ) { self::require_pluggable_essentials(); $wp_user = get_user_by( 'email', $user->email ); if ( $wp_user instanceof WP_User ) { $super_admins = get_super_admins(); $is_super_admin = ( is_array( $super_admins ) && in_array( $wp_user->user_login, $super_admins ) ); } } return $is_super_admin; } #---------------------------------------------------------------------------------- #region Plans & Licensing #---------------------------------------------------------------------------------- /** * Check if running premium plugin code. * * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @return bool */ function is_premium() { /** * `$this->_plugin` will be `false` when `is_activation_mode` calls this method directly from the * `register_constructor_hooks` method. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ return is_object( $this->_plugin ) ? $this->_plugin->is_premium : false; } /** * Get site's plan ID. * * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @return number */ function get_plan_id() { return $this->_site->plan_id; } /** * Get site's plan title. * * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @return string */ function get_plan_title() { $plan = $this->get_plan(); return is_object( $plan ) ? $plan->title : 'PLAN_TITLE'; } /** * Get site's plan name. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return string */ function get_plan_name() { $plan = $this->get_plan(); return is_object( $plan ) ? $plan->name : 'PLAN_NAME'; } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return FS_Plugin_Plan|false */ function get_plan() { if ( ! is_object( $this->_site ) ) { return false; } return FS_Plugin_Plan::is_valid_id( $this->_site->plan_id ) ? $this->_get_plan_by_id( $this->_site->plan_id ) : false; } /** * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @return bool */ function is_trial() { $this->_logger->entrance(); if ( ! $this->is_registered( true ) || ! is_object( $this->_site ) ) { return false; } return $this->_site->is_trial(); } /** * Check if currently in a trial with payment method (credit card or paypal). * * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @return bool */ function is_paid_trial() { $this->_logger->entrance(); if ( ! $this->is_trial() ) { return false; } if ( ! $this->has_active_valid_license() ) { return false; } if ( $this->_site->trial_plan_id != $this->_license->plan_id ) { return false; } /** * @var FS_Subscription $subscription */ $subscription = $this->_get_subscription( $this->_license->id ); return ( is_object( $subscription ) && $subscription->is_active() ); } /** * Check if trial already utilized. * * @since 1.0.9 * * @return bool */ function is_trial_utilized() { $this->_logger->entrance(); if ( ! $this->is_registered() ) { return false; } return $this->_site->is_trial_utilized(); } /** * Get trial plan information (if in trial). * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool|FS_Plugin_Plan */ function get_trial_plan() { $this->_logger->entrance(); if ( ! $this->is_trial() ) { return false; } // Try to load plan from local cache. $trial_plan = $this->_get_plan_by_id( $this->_site->trial_plan_id ); if ( ! is_object( $trial_plan ) ) { $trial_plan = $this->_fetch_site_plan( $this->_site->trial_plan_id ); /** * If managed to fetch the plan, add it to the plans collection. */ if ( $trial_plan instanceof FS_Plugin_Plan ) { if ( ! is_array( $this->_plans ) ) { $this->_plans = array(); } $this->_plans[] = $trial_plan; $this->_store_plans(); } } if ( $trial_plan instanceof FS_Plugin_Plan ) { return $trial_plan; } /** * If for some reason failed to get the trial plan, fallback to a dummy name and title. */ $trial_plan = new FS_Plugin_Plan(); $trial_plan->id = $this->_site->trial_plan_id; $trial_plan->name = 'pro'; $trial_plan->title = 'Pro'; return $trial_plan; } /** * Check if the user has an activate, non-expired license on current plugin's install. * * @since 1.0.9 * * @return bool */ function is_paying() { $this->_logger->entrance(); if ( ! $this->is_registered( true ) ) { return false; } if ( ! $this->has_paid_plan() ) { return false; } return ( ! $this->is_trial() && 'free' !== $this->get_plan_name() && $this->has_active_valid_license() ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @return bool */ function is_free_plan() { if ( ! $this->is_registered() ) { return true; } if ( ! $this->has_paid_plan() ) { return true; } return ( 'free' === $this->get_plan_name() || ! $this->has_features_enabled_license() ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @return bool */ function _has_premium_license() { $this->_logger->entrance(); $premium_license = $this->_get_available_premium_license(); return ( false !== $premium_license ); } /** * Check if user has any licenses associated with the plugin (including expired or blocking). * * @author Vova Feldman (@svovaf) * @since 1.1.7.3 * * @param bool $including_foreign * * @return bool */ function has_any_license( $including_foreign = true ) { if ( ! is_array( $this->_licenses ) || 0 === count( $this->_licenses ) ) { return false; } if ( $including_foreign ) { return true; } foreach ( $this->_licenses as $license ) { if ( $this->_user->id == $license->user_id ) { return true; } } return false; } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param bool|null $is_localhost * * @return FS_Plugin_License|false */ function _get_available_premium_license( $is_localhost = null ) { $this->_logger->entrance(); $licenses = $this->get_available_premium_licenses( $is_localhost ); if ( ! empty( $licenses ) ) { return $licenses[0]; } return false; } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param bool|null $is_localhost * * @return FS_Plugin_License[] */ function get_available_premium_licenses( $is_localhost = null ) { $this->_logger->entrance(); $licenses = array(); if ( ! $this->has_paid_plan() ) { return $licenses; } if ( is_array( $this->_licenses ) ) { foreach ( $this->_licenses as $license ) { if ( ! $license->can_activate( $is_localhost ) ) { continue; } $licenses[] = $license; } } return $licenses; } /** * Sync local plugin plans with remote server. * * IMPORTANT: If for some reason a site is associated with deleted plan, we'll preserve the plan's information and append it as the last plan. This means that if plan is deleted, the is_plan() method will ALWAYS return true for any given argument (it becomes the most inclusive plan). * * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @return FS_Plugin_Plan[]|object */ function _sync_plans() { $plans = $this->_fetch_plugin_plans(); if ( $this->is_array_instanceof( $plans, 'FS_Plugin_Plan' ) ) { $plans_map = array(); foreach ( $plans as $plan ) { $plans_map[ $plan->id ] = true; } $plans_ids_to_keep = $this->get_plans_ids_associated_with_installs(); foreach ( $plans_ids_to_keep as $plan_id ) { if ( isset( $plans_map[ $plan_id ] ) ) { continue; } $missing_plan = self::_get_plan_by_id( $plan_id ); if ( is_object( $missing_plan ) ) { $plans[] = $missing_plan; } } $this->_plans = $plans; $this->_store_plans(); } $this->do_action( 'after_plans_sync', $plans ); return $this->_plans; } /** * Check if specified plan exists locally. If not, fetch it and store it. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $plan_id * * @return \FS_Plugin_Plan|object The plan entity or the API error object on failure. */ private function sync_plan_if_not_exist( $plan_id ) { $plan = self::_get_plan_by_id( $plan_id ); if ( is_object( $plan ) ) { // Plan already exists. return $plan; } $plan = $this->fetch_plan_by_id( $plan_id ); if ( $plan instanceof FS_Plugin_Plan ) { $this->_plans[] = $plan; $this->_store_plans(); return $plan; } return $plan; } /** * Check if specified license exists locally. If not, fetch it and store it. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $license_id * @param string $license_key * * @return \FS_Plugin_Plan|object The plan entity or the API error object on failure. */ private function sync_license_if_not_exist( $license_id, $license_key ) { $license = $this->_get_license_by_id( $license_id ); if ( is_object( $license ) ) { // License already exists. return $license; } $license = $this->fetch_license_by_key( $license_id, $license_key ); if ( $license instanceof FS_Plugin_License ) { $this->_licenses[] = $license; $this->set_license( $license ); $this->_store_licenses(); return $license; } return $license; } /** * Get a collection of unique plan IDs that are associated with any installs in the network. * * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @return number[] */ private function get_plans_ids_associated_with_installs() { if ( ! is_multisite() ) { if ( ! is_object( $this->_site ) || ! FS_Plugin_Plan::is_valid_id( $this->_site->plan_id ) ) { return array(); } return array( $this->_site->plan_id ); } $plan_ids = array(); $sites = self::get_sites(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $install = $this->get_install_by_blog_id( $blog_id ); if ( ! is_object( $install ) || ! FS_Plugin_Plan::is_valid_id( $install->plan_id ) ) { continue; } $plan_ids[ $install->plan_id ] = true; } return array_keys( $plan_ids ); } /** * Get a collection of unique license IDs that are associated with any installs in the network. * * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @return number[] */ private function get_license_ids_associated_with_installs() { if ( ! $this->_is_network_active ) { if ( ! is_object( $this->_site ) || ! FS_Plugin_License::is_valid_id( $this->_site->license_id ) ) { return array(); } return array( $this->_site->license_id ); } $license_ids = array(); $sites = self::get_sites(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $install = $this->get_install_by_blog_id( $blog_id ); if ( ! is_object( $install ) || ! FS_Plugin_License::is_valid_id( $install->license_id ) ) { continue; } $license_ids[ $install->license_id ] = true; } return array_keys( $license_ids ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param number $id * * @return FS_Plugin_Plan|false */ function _get_plan_by_id( $id ) { $this->_logger->entrance(); if ( ! is_array( $this->_plans ) || 0 === count( $this->_plans ) ) { $this->_sync_plans(); } foreach ( $this->_plans as $plan ) { if ( $id == $plan->id ) { return $plan; } } return false; } /** * @author Vova Feldman (@svovaf) * @since 1.1.8.1 * * @param string $name * * @return FS_Plugin_Plan|false */ private function get_plan_by_name( $name ) { $this->_logger->entrance(); if ( ! is_array( $this->_plans ) || 0 === count( $this->_plans ) ) { $this->_sync_plans(); } foreach ( $this->_plans as $plan ) { if ( $name == $plan->name ) { return $plan; } } return false; } /** * Sync local licenses with remote server. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param number|bool $site_license_id * @param number|null $blog_id * * @return FS_Plugin_License[]|object */ function _sync_licenses( $site_license_id = false, $blog_id = null ) { $this->_logger->entrance(); $is_network_admin = fs_is_network_admin(); if ( $is_network_admin && is_null( $blog_id ) ) { $all_licenses = self::get_all_licenses( $this->_module_id ); } else { $all_licenses = $this->get_user_licenses( $this->_user->id ); } $foreign_licenses = $this->get_foreign_licenses_info( $all_licenses, $site_license_id ); $all_licenses_map = array(); foreach ( $all_licenses as $license ) { $all_licenses_map[ $license->id ] = true; } $licenses = $this->_fetch_licenses( false, $site_license_id, $foreign_licenses, $blog_id ); if ( $this->is_array_instanceof( $licenses, 'FS_Plugin_License' ) ) { $licenses_map = array(); foreach ( $licenses as $license ) { $licenses_map[ $license->id ] = true; } // $license_ids_to_keep = $this->get_license_ids_associated_with_installs(); // foreach ( $license_ids_to_keep as $license_id ) { // if ( isset( $licenses_map[ $license_id ] ) ) { // continue; // } // // $missing_license = self::_get_license_by_id( $license_id, false ); // if ( is_object( $missing_license ) ) { // $licenses[] = $missing_license; // $licenses_map[ $missing_license->id ] = true; // } // } $user_license_ids = $this->get_user_linked_license_ids( $this->_user->id ); foreach ( $user_license_ids as $key => $license_id ) { if ( ! isset( $licenses_map[ $license_id ] ) ) { // Remove access to licenses that no longer exist. unset( $user_license_ids[ $key ] ); } } if ( ! empty( $user_license_ids ) ) { foreach ( $licenses_map as $license_id => $value ) { if ( ! isset( $all_licenses_map[ $license_id ] ) ) { // Associate new licenses with the user who triggered the license syncing. $user_license_ids[] = $license_id; } } $user_license_ids = array_unique( $user_license_ids ); } else { $user_license_ids = array_keys( $licenses_map ); } if ( ! $is_network_admin || ! is_null( $blog_id ) ) { $user_licenses = array(); foreach ( $licenses as $license ) { if ( ! in_array( $license->id, $user_license_ids ) ) { continue; } $user_licenses[] = $license; } $this->_licenses = $user_licenses; } else { $this->_licenses = $licenses; } $this->set_user_linked_license_ids( $this->_user->id, $user_license_ids ); $this->_store_licenses( true, $this->_module_id, $licenses ); } // Update current license. if ( is_object( $this->_license ) ) { $license = $this->_get_license_by_id( $this->_license->id ); if ( is_object( $license ) ) { /** * `$license` can be `false` in case a user change action has just been completed and this method * has synced the `$this->_licenses` collection for the new user. In this case, the * `$this->_licenses` collection may have only the newly activated license that is associated with * the new user. `set_license` will eventually be called in the same request by the logic that * follows outside this method which will detect that the install's license has been updated, and * then `_update_site_license` will be called which in turn will call `set_license`. * * @author Leo Fajardo (@leorw) * @since 2.3.2 */ $this->set_license( $license ); } } return $this->_licenses; } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param number $id * @param bool $sync_licenses * * @return FS_Plugin_License|false */ function _get_license_by_id( $id, $sync_licenses = true ) { $this->_logger->entrance(); if ( ! FS_Plugin_License::is_valid_id( $id ) ) { return false; } /** * When running from the network level admin and opted-in from the network, * check if the license exists in the network user licenses collection. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ if ( fs_is_network_admin() && $this->is_network_registered() && ( ! is_object( $this->_user ) || $this->_storage->network_user_id != $this->_user->id ) ) { $licenses = $this->get_user_licenses( $this->_storage->network_user_id ); foreach ( $licenses as $license ) { if ( $id == $license->id ) { return $license; } } } if ( ! $this->has_any_license() && $sync_licenses ) { $this->_sync_licenses( $id ); } if ( is_array( $this->_licenses ) ) { foreach ( $this->_licenses as $license ) { if ( $id == $license->id ) { return $license; } } } return false; } /** * Get license by ID. Unlike _get_license_by_id(), this method only checks the local storage and return any license, whether it's associated with the current context user/install or not. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $id * * @return FS_Plugin_License */ private function get_license_by_id( $id ) { $licenses = self::get_all_licenses( $this->_module_id ); if ( is_array( $licenses ) && ! empty( $licenses ) ) { foreach ( $licenses as $license ) { if ( $id == $license->id ) { return $license; } } } return null; } /** * Synchronize the site's context license by fetching the license form the API and updating the local data with it. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return \FS_Plugin_License|mixed */ private function sync_site_license() { $api = $this->get_api_user_scope(); $result = $api->get( "/licenses/{$this->_license->id}.json?license_key=" . urlencode( $this->_license->secret_key ), true ); if ( ! $this->is_api_result_entity( $result ) ) { return $result; } $license = $this->_update_site_license( new FS_Plugin_License( $result ) ); $this->_store_licenses(); return $license; } /** * Get all user's available licenses for the current module. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $user_id * * @return FS_Plugin_License[] */ private function get_user_licenses( $user_id ) { $all_licenses = self::get_all_licenses( $this->_module_id ); if ( empty( $all_licenses ) ) { return array(); } $user_license_ids = $this->get_user_linked_license_ids( $user_id ); if ( empty( $user_license_ids ) ) { return array(); } $licenses = array(); foreach ( $all_licenses as $license ) { if ( in_array( $license->id, $user_license_ids ) ) { $licenses[] = $license; } } return $licenses; } /** * Checks if the context license is network activated except on the given blog ID. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $except_blog_id * * @return bool */ private function is_license_network_active( $except_blog_id = 0 ) { $this->_logger->entrance(); if ( ! is_object( $this->_license ) ) { return false; } $sites = self::get_sites(); if ( $this->_license->total_activations() < ( count( $sites ) - 1 ) ) { // There are more sites than the number of activations, so license cannot be network activated. return false; } foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); if ( $except_blog_id == $blog_id ) { // Skip excluded blog. continue; } $install = $this->get_install_by_blog_id( $blog_id ); if ( is_object( $install ) && $install->license_id != $this->_license->id ) { return false; } } return true; } /** * Checks if license can be activated on all the network sites (opted-in or skipped) that are not yet associated with a license. If possible, try to make the activation, if not return false. * * Notice: On success, this method will also update the license activations counters (without updating the license in the storage). * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_User $user * @param \FS_Plugin_License $license * * @return bool */ private function try_activate_license_on_network( FS_User $user, FS_Plugin_License $license ) { $this->_logger->entrance(); $result = $this->can_activate_license_on_network( $license ); if ( false === $result ) { return false; } $installs_without_license = $result['installs']; if ( ! empty( $installs_without_license ) ) { $this->activate_license_on_many_installs( $user, $license->secret_key, $installs_without_license ); } $disconnected_site_ids = $result['sites']; if ( ! empty( $disconnected_site_ids ) ) { $this->activate_license_on_many_sites( $user, $license->secret_key, $disconnected_site_ids ); } $this->link_license_2_user( $license->id, $user->id ); // Sync license after activations. $license->activated += $result['production_count']; $license->activated_local += $result['localhost_count']; // $this->_store_licenses() return true; } /** * Checks if the given license can be activated on the whole network. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_Plugin_License $license * * @return false|array { * @type array[int]FS_Site $installs Blog ID to install map. * @type int[] $sites Non-connected blog IDs. * @type int $production_count Production sites count. * @type int $localhost_count Production sites count. * } */ private function can_activate_license_on_network( FS_Plugin_License $license ) { $sites = self::get_sites(); $production_count = 0; $localhost_count = 0; $installs_without_license = array(); $disconnected_site_ids = array(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $install = $this->get_install_by_blog_id( $blog_id ); if ( is_object( $install ) ) { if ( FS_Plugin_License::is_valid_id( $install->license_id ) ) { // License already activated on the install. continue; } $url = $install->url; $installs_without_license[ $blog_id ] = $install; } else { $url = is_object( $site ) ? $site->siteurl : self::get_unfiltered_site_url( $blog_id ); $disconnected_site_ids[] = $blog_id; } if ( FS_Site::is_localhost_by_address( $url ) ) { $localhost_count ++; } else { $production_count ++; } } if ( ! $license->can_activate_bulk( $production_count, $localhost_count ) ) { return false; } return array( 'installs' => $installs_without_license, 'sites' => $disconnected_site_ids, 'production_count' => $production_count, 'localhost_count' => $localhost_count, ); } /** * Activate a given license on a collection of installs. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_User $user * @param string $license_key * @param array $blog_2_install_map { * @key int Blog ID. * @value FS_Site Blog's associated install. * } * * @return mixed|true */ private function activate_license_on_many_installs( FS_User $user, $license_key, array $blog_2_install_map ) { $params = array( array( 'license_key' => $this->apply_filters( 'license_key', $license_key ) ) ); $install_2_blog_map = array(); foreach ( $blog_2_install_map as $blog_id => $install ) { $params[] = array( 'id' => $install->id, 'url' => $install->url ); $install_2_blog_map[ $install->id ] = $blog_id; } $result = $this->get_api_user_scope_by_user( $user )->call( "plugins/{$this->_plugin->id}/installs.json", 'PUT', $params ); if ( ! $this->is_api_result_object( $result, 'installs' ) ) { return $result; } foreach ( $result->installs as $r_install ) { $install = new FS_Site( $r_install ); $install->is_disconnected = false; // Update install. $this->_store_site( true, $install_2_blog_map[ $r_install->id ], $install ); } return true; } /** * Activate a given license on a collection of blogs/sites that are not yet opted-in. * * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @param \FS_User $user * @param string $license_key * * @return true|mixed True if successful, otherwise, the API result. */ private function activate_license_on_site( FS_User $user, $license_key ) { return $this->activate_license_on_many_sites( $user, $license_key ); } /** * Activate a given license on a collection of blogs/sites that are not yet opted-in. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_User $user * @param string $license_key * @param int[] $site_ids * * @return true|mixed True if successful, otherwise, the API result. */ private function activate_license_on_many_sites( FS_User $user, $license_key, array $site_ids = array() ) { $sites = array(); foreach ( $site_ids as $site_id ) { $sites[] = $this->get_site_info( array( 'blog_id' => $site_id ) ); } // Install the plugin. $result = $this->create_installs_with_user( $user, $license_key, false, $sites, false, true ); if ( ! $this->is_api_result_entity( $result ) && ! $this->is_api_result_object( $result, 'installs' ) ) { return $result; } $installs = array(); if ( $this->is_api_result_entity( $result ) ) { $install = new FS_Site( $result ); $this->_user = $user; $this->_store_site( true, null, $install ); $this->_site = $install; $this->reset_anonymous_mode(); } else { foreach ( $result->installs as $install ) { $installs[] = new FS_Site( $install ); } // Map site addresses to their blog IDs. $address_to_blog_map = $this->get_address_to_blog_map(); $first_blog_id = null; foreach ( $installs as $install ) { $address = trailingslashit( fs_strip_url_protocol( $install->url ) ); $blog_id = $address_to_blog_map[ $address ]; $this->_store_site( true, $blog_id, $install ); $this->reset_anonymous_mode( $blog_id ); if ( is_null( $first_blog_id ) ) { $first_blog_id = $blog_id; } } if ( ! FS_Site::is_valid_id( $this->_storage->network_install_blog_id ) ) { $this->_storage->network_install_blog_id = $first_blog_id; } } return true; } /** * Sync site's license with user licenses. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param FS_Plugin_License|null $new_license * * @return FS_Plugin_License|null */ function _update_site_license( $new_license ) { $this->_logger->entrance(); /** * In case this call will be removed in the future, the `_sync_licenses()` method needs to be updated * accordingly so that it will also handle the case when an ownership change is done via license * activation. * * @author Leo Fajardo (@leorw) * @since 2.3.2 */ $this->set_license( $new_license ); if ( ! is_object( $new_license ) ) { $this->_site->license_id = null; $this->_sync_site_subscription( null ); return $this->_license; } $this->_site->license_id = $this->_license->id; if ( ! is_array( $this->_licenses ) ) { $this->_licenses = array(); } $is_license_found = false; for ( $i = 0, $len = count( $this->_licenses ); $i < $len; $i ++ ) { if ( $new_license->id == $this->_licenses[ $i ]->id ) { $this->_licenses[ $i ] = $new_license; $is_license_found = true; break; } } // If new license just append. if ( ! $is_license_found ) { $this->_licenses[] = $new_license; } $this->_sync_site_subscription( $new_license ); return $this->_license; } /** * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @param \FS_Plugin_License $license */ private function set_license( FS_Plugin_License $license = null ) { $this->_license = $license; $this->maybe_update_whitelabel_flag( $license ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @param FS_Plugin_License $license */ private function maybe_update_whitelabel_flag( $license ) { $is_whitelabeled = isset( $this->_storage->is_whitelabeled ) ? $this->_storage->is_whitelabeled : false; if ( is_object( $license ) ) { $license_user = self::_get_user_by_id( $license->user_id ); if ( ! is_object( $license_user ) ) { // If foreign license, do not update the `is_whitelabeled` flag. return; } if ( $this->is_addon() ) { /** * Store the last license data to the parent's storage since it's needed only when showing the * "Start Debug" dialog which is triggered from the "Account" page. This way, there's no need to * iterate over the add-ons just to get the last license data. */ $this->get_parent_instance()->store_last_activated_license_data( $license, $license_user ); } else { $this->store_last_activated_license_data( $license ); } if ( $license->is_whitelabeled ) { // Activated a developer license, data should be hidden. $is_whitelabeled = true; } else if ( $this->is_registered() && $this->_user->id == $license->user_id ) { // The account owner activated a regular license key, no need to hide the data. $is_whitelabeled = false; } } $this->_storage->is_whitelabeled = $is_whitelabeled; // Reset the whitelabeled status after update. $this->is_whitelabeled = null; if ( $this->is_addon() ) { $parent_fs = $this->get_parent_instance(); if ( is_object( $parent_fs ) ) { $parent_fs->is_whitelabeled = null; } } } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @param FS_Plugin_License $license * @param FS_User $license_user */ private function store_last_activated_license_data( FS_Plugin_License $license, $license_user = null ) { if ( ! is_object( $license_user ) ) { $this->_storage->last_license_key = md5( $license->secret_key ); $this->_storage->last_license_user_id = null; } else { $this->_storage->last_license_user_key = md5( $license_user->secret_key ); $this->_storage->last_license_user_id = $license_user->id; } } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @param bool $ignore_data_debug_mode * * @return bool */ function is_whitelabeled_by_flag( $ignore_data_debug_mode = false ) { if ( true !== $this->_storage->is_whitelabeled ) { return false; } else if ( $ignore_data_debug_mode ) { return true; } $fs = $this->is_addon() ? $this->get_parent_instance() : $this; return ! $fs->is_data_debug_mode(); } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @return number */ function get_last_license_user_id() { return ( FS_User::is_valid_id( $this->_storage->last_license_user_id ) ) ? $this->_storage->last_license_user_id : null; } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @param int $blog_id * @param bool $ignore_data_debug_mode * * @return bool */ function is_whitelabeled( $ignore_data_debug_mode = false, $blog_id = null ) { if ( ! is_null( $blog_id ) ) { $this->switch_to_blog( $blog_id ); } if ( ! is_null( $this->is_whitelabeled ) ) { $is_whitelabeled = $this->is_whitelabeled; } else { $is_whitelabeled = false; $is_whitelabeled_flag = $this->is_whitelabeled_by_flag( true ); if ( ! $this->has_addons() ) { $is_whitelabeled = $is_whitelabeled_flag; } else if ( $is_whitelabeled_flag ) { $is_whitelabeled = true; } else { if ( $this->is_registered() || $this->is_premium() ) { $addon_ids = $this->get_updated_account_addons(); } else { $addons = self::get_all_addons(); $plugin_addons = isset( $addons[ $this->_plugin->id ] ) ? $addons[ $this->_plugin->id ] : array(); $addon_ids = array(); foreach ( $plugin_addons as $addon ) { $addon_ids[] = $addon->id; } } $installed_addons = $this->get_installed_addons(); foreach ( $installed_addons as $fs_addon ) { $addon_ids[] = $fs_addon->get_id(); } if ( ! empty( $addon_ids ) ) { $addon_ids = array_unique( $addon_ids ); $is_network_level = ( fs_is_network_admin() && $this->is_network_active() ); foreach ( $addon_ids as $addon_id ) { $addon = $this->get_addon( $addon_id ); if ( ! is_object( $addon ) ) { continue; } $addon_storage = FS_Storage::instance( WP_FS__MODULE_TYPE_PLUGIN, $addon->slug ); $fs_addon = $this->is_addon_activated( $addon_id ) ? self::get_addon_instance( $addon_id ) : null; $was_addon_network_activated = false; if ( is_object( $fs_addon ) ) { $was_addon_network_activated = $fs_addon->is_network_active(); } else if ( $is_network_level ) { $was_addon_network_activated = $addon_storage->get( 'was_plugin_loaded', false, true ); } $network_delegated_connection = ( $was_addon_network_activated && $addon_storage->get( 'is_delegated_connection', false, true ) ); if ( $is_network_level && ( ! $was_addon_network_activated || $network_delegated_connection ) ) { $sites = self::get_sites(); /** * If in network admin area and the add-on was not network-activated or network-activated * and network-delegated, find any add-on whose is_whitelabeled flag is true. */ foreach ( $sites as $site ) { $site_info = $this->get_site_info( $site ); if ( $addon_storage->get( 'is_whitelabeled', false, $site_info['blog_id'] ) ) { $is_whitelabeled = true; break; } } if ( $is_whitelabeled ) { break; } } else { /** * This will be executed when any of the following is met: * 1. Add-on was network-activated, not network-delegated, and in network admin area. * 2. Add-on was network-activated, network-delegated, and in site admin area. * 3. Add-on was not network-activated and in site admin area. */ if ( true === $addon_storage->is_whitelabeled ) { $is_whitelabeled = true; break; } } } } } $this->is_whitelabeled = $is_whitelabeled; if ( ! $is_whitelabeled || ! $this->is_data_debug_mode() ) { $this->_admin_notices->remove_sticky( 'data_debug_mode_enabled' ); } if ( ! is_null( $blog_id ) ) { $this->restore_current_blog(); } } return ( $is_whitelabeled && ( $ignore_data_debug_mode || ! $this->is_data_debug_mode() ) ); } /** * Sync site's subscription. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param FS_Plugin_License|null $license * * @return bool|\FS_Subscription */ private function _sync_site_subscription( $license ) { if ( ! is_object( $license ) ) { $this->delete_unused_subscriptions(); return false; } // Load subscription details if not lifetime. $subscription = $license->is_lifetime() ? false : $this->_fetch_site_license_subscription(); if ( is_object( $subscription ) && ! isset( $subscription->error ) ) { $this->store_subscription( $subscription ); } else { $this->delete_unused_subscriptions(); } return $subscription; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool|\FS_Plugin_License */ function _get_license() { if ( ! fs_is_network_admin() || is_object( $this->_license ) ) { return $this->_license; } return $this->_get_available_premium_license(); } /** * @param number $license_id * * @return null|\FS_Subscription */ function _get_subscription( $license_id ) { if ( ! isset( $this->_storage->subscriptions ) || empty( $this->_storage->subscriptions ) ) { return null; } foreach ( fs_get_entities( $this->_storage->subscriptions, FS_Subscription::get_class_name() ) as $subscription ) { if ( $subscription->license_id == $license_id ) { return $subscription; } } return null; } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param FS_Subscription $subscription */ function store_subscription( FS_Subscription $subscription ) { if ( ! isset( $this->_storage->subscriptions ) ) { $this->_storage->subscriptions = array(); } if ( empty( $this->_storage->subscriptions ) || ! is_multisite() ) { $this->_storage->subscriptions = array( $subscription ); return; } $subscriptions = fs_get_entities( $this->_storage->subscriptions, FS_Subscription::get_class_name() ); $updated_subscription = false; foreach ( $subscriptions as $key => $existing_subscription ) { if ( $existing_subscription->id == $subscription->id ) { $subscriptions[ $key ] = $subscription; $updated_subscription = true; break; } } if ( ! $updated_subscription ) { $subscriptions[] = $subscription; } $this->_storage->subscriptions = $subscriptions; } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 */ function delete_unused_subscriptions() { if ( ! isset( $this->_storage->subscriptions ) || empty( $this->_storage->subscriptions ) || // Clean up only if there are already at least 3 subscriptions. ( count( $this->_storage->subscriptions ) < 3 ) ) { return; } if ( ! is_multisite() ) { // If not multisite, there should only be 1 subscription, so just clear the array. $this->_storage->subscriptions = array(); return; } $subscriptions_to_keep_by_license_id_map = array(); $sites = self::get_sites(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $install = $this->get_install_by_blog_id( $blog_id ); if ( ! is_object( $install ) || ! FS_Plugin_License::is_valid_id( $install->license_id ) ) { continue; } $subscriptions_to_keep_by_license_id_map[ $install->license_id ] = true; } if ( empty( $subscriptions_to_keep_by_license_id_map ) ) { $this->_storage->subscriptions = array(); return; } foreach ( $this->_storage->subscriptions as $key => $subscription ) { if ( ! isset( $subscriptions_to_keep_by_license_id_map[ $subscription->license_id ] ) ) { unset( $this->_storage->subscriptions[ $key ] ); } } } /** * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @param string $plan Plan name * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans. * * @return bool */ function is_plan( $plan, $exact = false ) { $this->_logger->entrance(); if ( ! $this->is_registered() ) { return false; } $plan = strtolower( $plan ); $current_plan_name = $this->get_plan_name(); if ( $current_plan_name === $plan ) { // Exact plan. return true; } else if ( $exact ) { // Required exact, but plans are different. return false; } $current_plan_order = - 1; $required_plan_order = PHP_INT_MAX; for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) { if ( $plan === $this->_plans[ $i ]->name ) { $required_plan_order = $i; } else if ( $current_plan_name === $this->_plans[ $i ]->name ) { $current_plan_order = $i; } } return ( $current_plan_order > $required_plan_order ); } /** * Check if module has only one plan. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param bool $double_check In some cases developers prefer to release their paid offering as premium-only, even though there is a free version. For those cases, looking at the 'is_premium_only' value isn't enough because the result will return false even when the product has only signle paid plan. * * @return bool */ function is_single_plan( $double_check = false ) { $this->_logger->entrance(); if ( ! $this->is_registered() || ! is_array( $this->_plans ) || 0 === count( $this->_plans ) ) { return true; } $has_free_plan = $this->has_free_plan(); if ( ! $has_free_plan && $double_check ) { foreach ( $this->_plans as $plan ) { if ( $plan->is_free() ) { $has_free_plan = true; break; } } } return ( 1 === ( count( $this->_plans ) - ( $has_free_plan ? 1 : 0 ) ) ); } /** * Check if plan based on trial. If not in trial mode, should return false. * * @since 1.0.9 * * @param string $plan Plan name * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans. * * @return bool */ function is_trial_plan( $plan, $exact = false ) { $this->_logger->entrance(); if ( ! $this->is_registered() ) { return false; } if ( ! $this->is_trial() ) { return false; } $trial_plan = $this->get_trial_plan(); if ( $trial_plan->name === $plan ) { // Exact plan. return true; } else if ( $exact ) { // Required exact, but plans are different. return false; } $current_plan_order = - 1; $required_plan_order = - 1; for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) { if ( $plan === $this->_plans[ $i ]->name ) { $required_plan_order = $i; } else if ( $trial_plan->name === $this->_plans[ $i ]->name ) { $current_plan_order = $i; } } return ( $current_plan_order > $required_plan_order ); } /** * Check if plugin has any paid plans. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return bool */ function has_paid_plan() { return $this->_has_paid_plans || FS_Plan_Manager::instance()->has_paid_plan( $this->_plans ); } /** * Check if plugin has any plan with a trail. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function has_trial_plan() { /** * @author Vova Feldman(@svovaf) * @since 1.2.1.5 * * Allow setting a trial from the SDK without calling the API. * But, if the user did opt-in, continue using the real data from the API. */ if ( $this->_trial_days >= 0 ) { return true; } return $this->_storage->get( 'has_trial_plan', false ); } /** * Check if plugin has any free plan, or is it premium only. * * Note: If no plans configured, assume plugin is free. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return bool */ function has_free_plan() { return ! $this->is_only_premium(); } /** * Displays a license activation dialog box when the user clicks on the "Activate License" * or "Change License" link on the plugins * page. * * @author Leo Fajardo (@leorw) * @since 1.1.9 */ function _add_license_activation_dialog_box() { $vars = array( 'id' => $this->_module_id, ); fs_require_template( 'forms/license-activation.php', $vars ); fs_require_template( 'forms/resend-key.php', $vars ); } /** * Displays an email address update dialog box when the user clicks on the email address "Edit" button on the "Account" page. * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function _add_email_address_update_dialog_box() { $vars = array( 'id' => $this->_module_id ); fs_require_template( 'forms/email-address-update.php', $vars ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function _add_email_address_update_option() { if ( ! $this->should_handle_user_change() ) { return; } // Add email address update AJAX handler. $this->add_ajax_action( 'update_email_address', array( &$this, '_email_address_update_ajax_handler' ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 */ function _email_address_update_ajax_handler() { $this->check_ajax_referer( 'update_email_address' ); $new_email_address = fs_request_get( 'email_address' ); $transfer_type = fs_request_get( 'transfer_type' ); $result = $this->update_email( $new_email_address ); if ( ! FS_Api::is_api_error( $result ) ) { self::shoot_ajax_success(); } $error = ''; if ( FS_Api::is_api_error_object( $result ) ) { switch ( $result->error->code ) { case 'user_exist': case 'account_verification_required': $error = array( 'code' => 'change_ownership', 'url' => $this->get_account_url( 'change_owner', array( 'state' => 'init', 'candidate_email' => $new_email_address, 'transfer_type' => $transfer_type, ) ), ); break; } } if ( empty( $error ) ) { $error = is_object( $result ) ? var_export( $result->error, true ) : $result; } self::shoot_ajax_failure( $error ); } /** * Returns a collection of IDs of installs that are associated with the context product and its add-ons, and activated with foreign licenses. * * @author Leo Fajardo (@leorw) * @since 2.3.2 * * @return number[] */ function get_installs_ids_with_foreign_licenses() { $installs = array(); if ( is_object( $this->_license ) && $this->_site->user_id != $this->_license->user_id ) { $installs[] = $this->_site->id; } /** * Also try to get foreign licenses for the context product's add-ons. */ $installs_by_slug_map = $this->get_parent_and_addons_installs_info(); foreach ( $installs_by_slug_map as $slug => $install_info ) { if ( $slug == $this->get_slug() ) { continue; } $install = $install_info['install']; $license = $install_info['license']; if ( is_object( $license ) && $install->user_id != $license->user_id ) { $installs[] = $install->id; } } return $installs; } /** * Displays the "Change User" dialog box when the user clicks on the "Change User" button on the "Account" page. * * @author Leo Fajardo (@leorw) * @since 2.3.2 * * @param number[] $install_ids */ function _add_user_change_dialog_box( $install_ids ) { $vars = array( 'id' => $this->_module_id, 'license_owners' => $this->fetch_installs_licenses_owners_data( $install_ids ) ); fs_require_template( 'forms/user-change.php', $vars ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 */ function _add_data_debug_mode_dialog_box() { $vars = array( 'id' => $this->_module_id, ); fs_require_template( 'forms/data-debug-mode.php', $vars ); } /** * Displays a subscription cancellation dialog box when the user clicks on the "Deactivate License" * link on the "Account" page or deactivates a plugin and there's an active subscription that is * either associated with a non-lifetime single-site license or non-lifetime multisite license that * is only activated on a single production site. * * @author Leo Fajardo (@leorw) * @since 2.2.1 * * @param bool $is_license_deactivation * * @return array */ function _get_subscription_cancellation_dialog_box_template_params( $is_license_deactivation = false ) { if ( fs_is_network_admin() ) { // Subscription cancellation dialog box is currently not supported for multisite networks. return array(); } if ( $this->is_whitelabeled() ) { return array(); } $license = $this->_get_license(); /** * If the installation is associated with a non-lifetime license, which is either a single-site or only activated on a single production site (or zero), and connected to an active subscription, suggest the customer to cancel the subscription upon deactivation. * * @author Leo Fajardo (@leorw) (Comment added by Vova Feldman @svovaf) * @since 2.2.1 */ if ( ! is_object( $license ) || $license->is_lifetime() || ( ! $license->is_single_site() && $license->activated > 1 ) ) { return array(); } /** * @var FS_Subscription $subscription */ $subscription = $this->_get_subscription( $license->id ); if ( ! is_object( $subscription ) || ! $subscription->is_active() ) { return array(); } return array( 'id' => $this->_module_id, 'license' => $license, 'has_trial' => $this->is_paid_trial(), 'is_license_deactivation' => $is_license_deactivation, ); } /** * @author Leo Fajardo (@leorw) * @since 2.0.2 */ function _add_premium_version_upgrade_selection_dialog_box() { $modules_update = get_site_transient( $this->is_theme() ? 'update_themes' : 'update_plugins' ); if ( ! isset( $modules_update->response[ $this->_plugin_basename ] ) ) { return; } $vars = array( 'id' => $this->_module_id, 'new_version' => is_object( $modules_update->response[ $this->_plugin_basename ] ) ? $modules_update->response[ $this->_plugin_basename ]->new_version : $modules_update->response[ $this->_plugin_basename ]['new_version'] ); fs_require_template( 'forms/premium-versions-upgrade-metadata.php', $vars ); fs_require_once_template( 'forms/premium-versions-upgrade-handler.php', $vars ); } /** * Displays the opt-out dialog box when the user clicks on the "Opt Out" link on the "Plugins" * page. * * @author Leo Fajardo (@leorw) * @since 1.2.1.5 */ function _add_optout_dialog() { if ( $this->is_theme() ) { $vars = null; fs_require_once_template( '/js/jquery.content-change.php', $vars ); } $vars = array( 'id' => $this->_module_id ); fs_require_template( 'forms/optout.php', $vars ); } /** * Prepare page to include all required UI and logic for the license activation dialog. * * @author Vova Feldman (@svovaf) * @since 1.2.0 */ function _add_license_activation() { if ( $this->is_migration() ) { return; } if ( ! $this->is_user_admin() ) { // Only admins can activate a license. return; } if ( ! $this->has_paid_plan() ) { // Module doesn't have any paid plans. return; } if ( $this->has_premium_version() && ! $this->is_premium() && /** * Also handle the case when an upgrade was made using the free version. * * @author Leo Fajardo (@leorw) * @since 2.3.2 */ ! is_object( $this->_get_license() ) ) { // Only add license activation logic to the premium version, or in case of a serviceware plugin, also in the free version. return; } // Add license activation link and AJAX request handler. if ( self::is_plugins_page() ) { $is_network_admin = fs_is_network_admin(); if ( ( $is_network_admin && $this->is_network_active() && ! $this->is_network_delegated_connection() ) || ( ! $is_network_admin && ( ! $this->is_network_active() || $this->is_delegated_connection() ) ) ) { if ( $this->is_premium() || ( $this->has_paid_plan() && ! $this->has_premium_version() ) ) { /** * @since 1.2.0 Add license action link only on plugins page. */ $this->_add_license_action_link(); } } } // Add license activation AJAX callback. $this->add_ajax_action( 'activate_license', array( &$this, '_activate_license_ajax_action' ) ); // Add resend license AJAX callback. $this->add_ajax_action( 'resend_license_key', array( &$this, '_resend_license_key_ajax_action' ) ); } /** * Prepares page to include all required UI and logic for the "Change User" dialog. * * @author Leo Fajardo (@leorw) * @since 2.3.2 */ function _add_user_change_option() { if ( ! $this->should_handle_user_change() ) { return; } $installs_ids_with_foreign_licenses = $this->get_installs_ids_with_foreign_licenses(); if ( empty( $installs_ids_with_foreign_licenses ) ) { // Handle user change only when the parent product or one of its add-ons is activated with a foreign license. return; } // Add user change AJAX handler. $this->add_ajax_action( 'change_user', array( &$this, '_user_change_ajax_action' ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.2 */ function should_handle_user_change() { if ( ! $this->is_user_admin() ) { // Only admins can change user. return false; } if ( $this->is_addon() ) { return false; } if ( ! $this->is_registered() ) { return false; } if ( $this->is_network_active() && ( fs_is_network_admin() || ! $this->is_site_delegated_connection() ) ) { // Handle only on site-level "Account" section for now. return false; } return true; } /** * @author Leo Fajardo (@leorw) * @since 2.0.2 */ function _add_premium_version_upgrade_selection() { if ( ! $this->is_user_admin() ) { return; } if ( ! $this->is_premium() || $this->has_any_active_valid_license() ) { // This is relevant only to the free versions and premium versions without an active license. return; } if ( self::is_updates_page() || ( $this->is_plugin() && self::is_plugins_page() ) ) { $this->_add_premium_version_upgrade_selection_action(); } } /** * @author Edgar Melkonyan * @since 2.4.1 * * @throws Freemius_Exception */ function _toggle_whitelabel_mode_ajax_handler() { $this->_logger->entrance(); $this->check_ajax_referer( 'toggle_whitelabel_mode' ); if ( ! $this->is_user_admin() ) { // Only for admins. self::shoot_ajax_failure(); } $license = $this->get_api_user_scope()->call( "/licenses/{$this->_site->license_id}.json", 'put', array( 'is_whitelabeled' => ! $this->_license->is_whitelabeled ) ); if ( ! $this->is_api_result_entity( $license ) ) { self::shoot_ajax_failure( FS_Api::is_api_error_object( $license ) ? $license->error->message : fs_text_inline( "An unknown error has occurred while trying to toggle the license's white-label mode.", 'unknown-error-occurred', $this->get_slug() ) ); } $this->_license->is_whitelabeled = $license->is_whitelabeled; $this->_store_licenses(); $this->_sync_license(); if ( ! $license->is_whitelabeled ) { $this->_admin_notices->remove_sticky( 'license_whitelabeled' ); } else { $this->_admin_notices->add_sticky( sprintf( $this->get_text_inline( 'Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s.', 'license_whitelabeled' ), "{$this->get_plugin_title()}", sprintf( '%s', $this->get_text_inline( 'User Dashboard', 'user-dashboard' ) ), sprintf( '%s', $this->get_text_inline( 'revert it now', 'revert-it-now' ) ) ), 'license_whitelabeled' ); } self::shoot_ajax_response( array( 'success' => true ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 */ function _add_beta_mode_update_handler() { if ( ! $this->is_user_admin() ) { return; } if ( ! $this->is_premium() ) { return; } $this->add_ajax_action( 'set_beta_mode', array( &$this, '_set_beta_mode_ajax_handler' ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 */ function _set_beta_mode_ajax_handler() { $this->_logger->entrance(); $this->check_ajax_referer( 'set_beta_mode' ); if ( ! $this->is_user_admin() ) { // Only for admins. self::shoot_ajax_failure(); } $is_beta = trim( fs_request_get( 'is_beta', '', 'post' ) ); if ( empty( $is_beta ) || ! in_array( $is_beta, array( 'true', 'false' ) ) ) { self::shoot_ajax_failure(); } $site = $this->api_site_call( '', 'put', array( 'is_beta' => ( 'true' == $is_beta ), 'fields' => 'is_beta' ) ); if ( ! $this->is_api_result_entity( $site ) ) { self::shoot_ajax_failure( FS_Api::is_api_error_object( $site ) ? $site->error->message : fs_text_inline( "An unknown error has occurred while trying to set the user's beta mode.", 'unknown-error-occurred', $this->get_slug() ) ); } $this->_site->is_beta = $site->is_beta; $this->_store_site(); self::shoot_ajax_response( array( 'success' => true ) ); } /** * License activation WP AJAX handler. * * @author Leo Fajardo (@leorw) * @since 1.1.9 * * @uses Freemius::activate_license() */ function _activate_license_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'activate_license' ); $license_key = trim( fs_request_get_raw( 'license_key' ) ); if ( empty( $license_key ) ) { $license_id = trim( fs_request_get_raw( 'license_id' ) ); if ( FS_Plugin_License::is_valid_id( $license_id ) ) { $license = $this->_get_license_by_id( $license_id, false ); if ( is_object( $license ) ) { $license_key = $license->secret_key; } } } if ( empty( $license_key ) ) { exit; } $sites = fs_is_network_admin() ? fs_request_get( 'sites', array(), 'post' ) : array(); $result = $this->activate_license( $license_key, $sites, fs_request_get_bool( 'is_marketing_allowed', null ), fs_request_get( 'blog_id', null ), fs_request_get( 'module_id', null, 'post' ), fs_request_get( 'user_id', null ), fs_request_get_bool( 'is_extensions_tracking_allowed', null ), fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ) ); if ( $result['success'] && $this->is_bundle_license_auto_activation_enabled() ) { $license = new FS_Plugin_License(); $license->secret_key = $license_key; $this->maybe_activate_bundle_license( $license, $sites ); } echo json_encode( $result ); exit; } /** * User change WP AJAX handler. * * @author Leo Fajardo (@leorw) * @since 2.3.2 */ function _user_change_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'change_user' ); $new_email_address = trim( fs_request_get( 'email_address', '' ) ); $new_user_id = fs_request_get( 'user_id' ); if ( empty( $new_email_address ) && ! FS_User::is_valid_id( $new_user_id ) ) { self::shoot_ajax_failure( fs_text_inline( 'Invalid new user ID or email address.', 'invalid-new-user-id-or-email', $this->get_slug() ) ); } $params = array(); if ( ! empty( $new_email_address ) ) { $params['user_email'] = $new_email_address; } else { $params['user_id'] = $new_user_id; } $installs_info_by_slug_map = $this->get_parent_and_addons_installs_info(); $install_ids = array(); foreach ( $installs_info_by_slug_map as $slug => $install_info ) { $install_ids[ $slug ] = $install_info['install']->id; } $params['install_ids'] = implode( ',', array_values( $install_ids ) ); $install = $this->get_api_site_scope()->call( $this->add_show_pending( '/' ), 'put', $params ); if ( FS_Api::is_api_error( $install ) ) { $error = ''; if ( is_object( $install ) ) { switch ( $install->error->code ) { case 'user_exist': $error = ( $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...' . $this->get_text_inline( 'Sorry, we could not complete the email update. Another user with the same email is already registered.', 'user-exist-message' ) . ' ' . sprintf( $this->get_text_inline( 'If you would like to give up the ownership of the %s\'s account to %s click the Change Ownership button.', 'user-exist-message_ownership' ), $this->_module_type, '' . $new_email_address . '' ) . sprintf( '', $this->get_account_url( 'change_owner', array( 'state' => 'init', 'candidate_email' => $new_email_address ) ), $this->get_text_inline( 'Change Ownership', 'change-ownership' ) ) ); break; } } if ( empty( $error ) ) { $error = FS_Api::is_api_error_object( $install ) ? $install->error->message : var_export( $install->error, true ); } self::shoot_ajax_failure( $error ); } else { if ( // If successful ownership change. $this->get_user()->id != $install->user_id || ! empty( $new_email_address ) ) { $this->complete_ownership_change_by_license( $install->user_id, $install_ids ); } } self::shoot_ajax_success(); } /** * @author Leo Fajardo (@leorw) * @since 2.3.2.14 */ function starting_migration() { if ( ! empty( $this->_storage->license_migration ) ) { // Do not overwrite the data if already set. return; } $this->_storage->license_migration = array( 'is_migrating' => true, 'start_timestamp' => time() ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.2.14 */ function is_migration() { if ( $this->is_addon() ) { return $this->get_parent_instance()->is_migration(); } if ( empty( $this->_storage->license_migration ) ) { return false; } if ( ! $this->_storage->license_migration['is_migrating'] ) { return false; } return ( // Return `true` if the migration is within 5 minutes from the starting time. ( time() - $this->_storage->license_migration['start_timestamp'] ) <= WP_FS__TIME_5_MIN_IN_SEC ); } /** * * A helper method to activate migrated licenses. If the product is network activated and integrated, the method will network activate the license. * * @author Vova Feldman (@svovaf) * @since 2.3.0 * * @param string $license_key * @param null|bool $is_marketing_allowed * @param null|number $plugin_id * @param array $sites * @param int $blog_id * * @return array { * @var bool $success * @var string $error * @var string $next_page * } * * @uses Freemius::activate_license() */ function activate_migrated_license( $license_key, $is_marketing_allowed = null, $plugin_id = null, $sites = array(), $blog_id = null ) { $this->_logger->entrance(); $result = $this->activate_license( $license_key, ( empty( $sites ) && is_null( $blog_id ) && $this->is_network_active() ) ? $this->get_sites_for_network_level_optin() : $sites, $is_marketing_allowed, $blog_id, $plugin_id ); // No need to show the sticky after license activation notice after migrating a license. $this->_admin_notices->remove_sticky( 'plan_upgraded' ); return $result; } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @return string */ function get_pricing_js_path() { if ( ! isset( $this->_pricing_js_path ) ) { $default_path = WP_FS__DIR_JS . '/pricing/freemius-pricing.js'; $pricing_js_path = $this->apply_filters( 'freemius_pricing_js_path', $default_path ); // Backward compatibility for people who placed the freemius-pricing inside `includes` directory. Let it take more preference than the default path. if ( empty( $pricing_js_path ) ) { global $fs_active_plugins; foreach ( $fs_active_plugins->plugins as $sdk_path => $data ) { if ( $data->plugin_path == $this->get_plugin_basename() ) { $plugin_or_theme_root_dir = ( $this->is_plugin() ? WP_PLUGIN_DIR : get_theme_root( get_stylesheet() ) ); $pricing_js_path = $plugin_or_theme_root_dir . '/' // The basename will be `plugins`, `themes`, or the basename of a custom plugins or themes directory. . str_replace( '../' . basename( $plugin_or_theme_root_dir ) . '/', '', $sdk_path ) . '/includes/freemius-pricing/freemius-pricing.js'; break; } } } // If it is still empty, load the default pricing JS. if ( ! file_exists( $pricing_js_path ) ) { $pricing_js_path = $default_path; } $this->_pricing_js_path = $pricing_js_path; } return $this->_pricing_js_path; } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 * * @deprecated Since v2.9.0 we have removed the iFrame based pricing. This will always return `false`. * * @return bool */ function should_use_external_pricing() { return false; } /** * The implementation of this method was previously in `_activate_license_ajax_action()`. * * @author Vova Feldman (@svovaf) * @since 2.2.4 * @since 2.0.0 When a super-admin that hasn't connected before is network activating a license and excluding some of the sites for the license activation, go over the unselected sites in the network and if a site is not connected, skipped, nor delegated, if it's a freemium product then just skip the connection for the site, if it's a premium only product, delegate the connection and license activation to the site admin (Vova Feldman @svovaf). * @param string $license_key * @param array $sites * @param null|bool $is_marketing_allowed * @param null|int $blog_id * @param null|number $plugin_id * @param null|number $license_owner_id * @param bool|null $is_extensions_tracking_allowed * @param bool|null $is_diagnostic_tracking_allowed Since 2.5.0.2 to allow license activation with minimal data footprint. * * * @return array { * @var bool $success * @var string $error * @var string $next_page * } */ private function activate_license( $license_key, $sites = array(), $is_marketing_allowed = null, $blog_id = null, $plugin_id = null, $license_owner_id = null, $is_extensions_tracking_allowed = null, $is_diagnostic_tracking_allowed = null ) { $this->_logger->entrance(); $license_key = trim( $license_key ); $is_network_activation_or_migration = ( fs_is_network_admin() || ( ! empty( $sites ) && $this->is_migration() ) ); if ( ! $is_network_activation_or_migration ) { // If the license activation is executed outside the context of a network admin, ignore the sites collection. $sites = array(); } $fs = ( empty($plugin_id) || $plugin_id == $this->_module_id ) ? $this : $this->get_addon_instance( $plugin_id ); FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array( FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed, FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed, ) ); $error = false; $next_page = false; $has_valid_blog_id = is_numeric( $blog_id ); $user = null; if ( $fs->is_addon() && $fs->get_parent_instance()->is_registered() ) { /** * When activating an add-on's license and the parent is opted-in, activate the license with the parent's opted-in user context. * * @author Vova Feldman (@svovaf) */ $user = $fs->get_parent_instance()->get_current_or_network_user(); } else if ( $fs->is_registered() ) { $user = $fs->get_current_or_network_user(); } if ( $has_valid_blog_id ) { /** * If a specific blog ID was provided, activate the license only on the specific blog that is associated with the given blog ID. * * @author Leo Fajardo (@leorw) */ $fs->switch_to_blog( $blog_id ); } if ( is_object( $user ) ) { $result = true; if ( $is_network_activation_or_migration && ! $has_valid_blog_id ) { // If no specific blog ID was provided, activate the license for all sites in the network. $blog_2_install_map = array(); $site_ids = array(); foreach ( $sites as $site ) { if ( ! isset( $site['blog_id'] ) || ! is_numeric( $site['blog_id'] ) ) { continue; } $install = $fs->get_install_by_blog_id( $site['blog_id'] ); if ( is_object( $install ) ) { $blog_2_install_map[ $site['blog_id'] ] = $install; } else { $site_ids[] = $site['blog_id']; } } if ( ! empty( $blog_2_install_map ) ) { $result = $fs->activate_license_on_many_installs( $user, $license_key, $blog_2_install_map ); } if ( true === $result && ! empty( $site_ids ) ) { $result = $fs->activate_license_on_many_sites( $user, $license_key, $site_ids ); } } else { if ( $fs->is_registered() ) { $params = array( 'license_key' => $fs->apply_filters( 'license_key', $license_key ) ); $install_ids = array(); $change_owner = FS_User::is_valid_id( $license_owner_id ); if ( $change_owner ) { $params['user_id'] = $license_owner_id; $installs_info_by_slug_map = $fs->get_parent_and_addons_installs_info(); foreach ( $installs_info_by_slug_map as $slug => $install_info ) { $install_ids[ $slug ] = $install_info['install']->id; } $params['install_ids'] = implode( ',', array_values( $install_ids ) ); } $api = $fs->get_api_site_scope(); $result = $api->call( $fs->add_show_pending( '/' ), 'put', $params ); if ( ! FS_Api::is_api_error( $result ) ) { $install = $result; $fs->reconnect_locally( $has_valid_blog_id ); if ( $change_owner && // If successful ownership change. $fs->get_user()->id != $install->user_id ) { $fs->complete_ownership_change_by_license( $install->user_id, $install_ids ); } } } else /* ( $fs->is_addon() && $fs->get_parent_instance()->is_registered() ) */ { $result = $fs->activate_license_on_site( $user, $license_key ); } } $is_connected = null; if ( true !== $result && ! FS_Api::is_api_result_entity( $result ) ) { if ( FS_Api::is_blocked( $result ) ) { $result->error->message = $this->generate_api_blocked_notice_message_from_result( $result ); $is_connected = false; } $error = FS_Api::is_api_error_object( $result ) ? $result->error->message : var_export( $result, true ); } else { $is_connected = true; $fs->network_upgrade_mode_completed(); $fs->_user = $user; if ( fs_is_network_admin() && ! $has_valid_blog_id ) { $fs->_site = $fs->get_network_install(); } $fs->_sync_license( true, $has_valid_blog_id ); $this->maybe_sync_install_user(); $next_page = $fs->is_addon() ? $fs->get_parent_instance()->get_account_url() : $fs->get_after_activation_url( 'after_connect_url' ); } $fs->update_connectivity_info( $is_connected ); } else { $next_page = $fs->opt_in( false, false, false, $license_key, false, false, false, $is_marketing_allowed, $sites ); if ( isset( $next_page->error ) ) { $error = $next_page->error; } else { if ( $is_network_activation_or_migration ) { /** * Get the list of sites that were just opted-in (and license activated). * This is an optimization for the next part below saving some DB queries. */ $connected_sites = array(); foreach ( $sites as $site ) { if ( isset( $site['blog_id'] ) && is_numeric( $site['blog_id'] ) ) { $connected_sites[ $site['blog_id'] ] = true; } } $all_sites = self::get_sites(); $pending_blog_ids = array(); /** * Check if there are any sites that are not connected, skipped, nor delegated. For every site that falls into that category, if the product is freemium, skip the connection. If the product is premium only, delegate the connection to the site administrator. * * @author Vova Feldman (@svovaf) */ foreach ( $all_sites as $site ) { $blog_id = self::get_site_blog_id( $site ); if ( isset( $connected_sites[ $blog_id ] ) ) { // Site was just connected. continue; } if ( $fs->is_installed_on_site( $blog_id ) ) { // Site was already connected before. continue; } if ( $fs->is_site_delegated_connection( $blog_id ) ) { // Site's connection was delegated. continue; } if ( $fs->is_anonymous_site( $blog_id ) ) { // Site connection was already skipped. continue; } $pending_blog_ids[] = $blog_id; } if ( ! empty( $pending_blog_ids ) ) { if ( $fs->is_freemium() && $fs->is_enable_anonymous() ) { $fs->skip_connection( $pending_blog_ids ); } else { $fs->delegate_connection( $pending_blog_ids ); } } } } } if ( false === $error && true === $fs->_storage->require_license_activation ) { $fs->_storage->require_license_activation = false; } $result = array( 'success' => ( false === $error ) ); if ( false !== $error ) { $result['error'] = $fs->apply_filters( 'opt_in_error_message', $error ); } else { if ( $fs->is_addon() || $fs->has_addons() ) { /** * Purge the valid user licenses cache so that when the "Account" or the "Add-Ons" page is loaded, * an updated valid user licenses collection will be fetched from the server which is used to also * update the account add-ons (add-ons the user has licenses for). * * @author Leo Fajardo (@leorw) * @since 2.2.4 */ $fs->purge_valid_user_licenses_cache(); } $result['next_page'] = $next_page; } return $result; } /** * @author Leo Fajardo (@leorw) * @since 2.3.2 * * @return array { * @key string Product slug. * @value array { * @property FS_Site $site * @property FS_Plugin_License $license * } * } */ private function get_parent_and_addons_installs_info() { $fs = $this->is_addon() ? $this->get_parent_instance() : $this; $installed_addons_ids = array(); $installed_addons_instances = $fs->get_installed_addons(); foreach ( $installed_addons_instances as $instance ) { $installed_addons_ids[] = $instance->get_id(); } $addons_ids = array_unique( array_merge( $installed_addons_ids, $fs->get_updated_account_addons() ) ); // Add parent product info. $installs_info_by_slug_map = array( $fs->get_slug() => array( 'install' => $fs->get_site(), 'license' => $fs->_get_license() ) ); foreach ( $addons_ids as $addon_id ) { $is_installed = isset( $installed_addons_ids_map[ $addon_id ] ); $addon_info = $fs->_get_addon_info( $addon_id, $is_installed ); if ( ! isset( $addon_info['is_connected'] ) || ! $addon_info['is_connected'] ) { // Add-on is not associated with an install entity. continue; } $installs_info_by_slug_map[ $addon_info['slug'] ] = array( 'install' => $addon_info['site'], 'license' => isset( $addon_info['license'] ) ? $addon_info['license'] : null ); } return $installs_info_by_slug_map; } /** * @author Leo Fajardo (@leorw) * @since 1.2.3.1 */ function _network_activate_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'network_activate' ); $plugin_id = fs_request_get( 'module_id', '', 'post' ); $fs = ( $plugin_id == $this->_module_id ) ? $this : $this->get_addon_instance( $plugin_id ); $error = false; $sites = fs_request_get( 'sites', array(), 'post' ); if ( is_array( $sites ) && ! empty( $sites ) ) { $sites_by_action = array( 'allow' => array(), 'delegate' => array(), 'skip' => array() ); foreach ( $sites as $site ) { $sites_by_action[ $site['action'] ][] = $site; } $total_sites = count( $sites ); $total_sites_to_delegate = count( $sites_by_action['delegate'] ); $next_page = ''; $has_any_install = fs_request_get_bool( 'has_any_install' ); if ( $total_sites === $total_sites_to_delegate && ! $this->is_network_upgrade_mode() && ! $has_any_install ) { $this->delegate_connection(); } else { if ( ! empty( $sites_by_action['delegate'] ) ) { $this->delegate_connection( self::get_sites_blog_ids( $sites_by_action['delegate'] ) ); } if ( ! empty( $sites_by_action['skip'] ) ) { $this->skip_connection( self::get_sites_blog_ids( $sites_by_action['skip'] ) ); } if ( empty( $sites_by_action['allow'] ) ) { if ( $has_any_install ) { $first_install = $fs->find_first_install(); if ( ! is_null( $first_install ) ) { $fs->_site = $first_install['install']; $fs->_storage->network_install_blog_id = $first_install['blog_id']; $fs->_user = self::_get_user_by_id( $fs->_site->user_id ); $fs->_storage->network_user_id = $fs->_user->id; } } } else { if ( ! $fs->is_registered() || ! $this->_is_network_active ) { $next_page = $fs->opt_in( false, false, false, false, false, false, false, fs_request_get_bool( 'is_marketing_allowed', null ), $sites_by_action['allow'] ); } else { $next_page = $fs->install_with_user( $this->get_network_user(), false, false, false, true, $sites_by_action['allow'] ); } if ( is_object( $next_page ) && isset( $next_page->error ) ) { $error = $next_page->error; } } } if ( empty( $next_page ) ) { $next_page = $this->get_after_activation_url( 'after_network_activation_url' ); } } else { $error = $this->get_text_inline( 'Invalid site details collection.', 'invalid_site_details_collection' ); } $result = array( 'success' => ( false === $error ) ); if ( false !== $error ) { $result['error'] = $error; } else { $result['next_page'] = $next_page; } echo json_encode( $result ); exit; } /** * Billing update AJAX callback. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 */ function _update_billing_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'update_billing' ); if ( ! $this->is_user_admin() ) { // Only for admins. self::shoot_ajax_failure(); } $billing = fs_request_get( 'billing' ); $api = $this->get_api_user_scope(); $result = $api->call( '/billing.json', 'put', array_merge( $billing, array( 'plugin_id' => $this->get_parent_id(), ) ) ); if ( ! $this->is_api_result_entity( $result ) ) { self::shoot_ajax_failure(); } // Purge cached billing. $this->get_api_user_scope()->purge_cache( 'billing.json' ); self::shoot_ajax_success(); } /** * Trial start for anonymous users (AJAX callback). * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 */ function _start_trial_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'start_trial' ); if ( ! $this->is_user_admin() ) { // Only for admins. self::shoot_ajax_failure(); } $trial_data = fs_request_get( 'trial' ); $next_page = $this->opt_in( false, false, false, false, false, $trial_data['plan_id'] ); if ( is_object( $next_page ) && $this->is_api_error( $next_page ) ) { self::shoot_ajax_failure( isset( $next_page->error ) ? $next_page->error->message : var_export( $next_page, true ) ); } $this->shoot_ajax_success( array( 'next_page' => $next_page, ) ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.0 */ function _resend_license_key_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'resend_license_key' ); $email_address = sanitize_email( trim( fs_request_get( 'email', '', 'post' ) ) ); if ( empty( $email_address ) ) { exit; } $error = false; $api = $this->get_api_plugin_scope(); $result = $api->call( '/licenses/resend.json', 'post', array( 'email' => $email_address, 'url' => home_url(), ) ); if ( is_object( $result ) && isset( $result->error ) ) { $error = $result->error; if ( in_array( $error->code, array( 'invalid_email', 'no_user' ) ) ) { $error = $this->get_text_inline( "We couldn't find your email address in the system, are you sure it's the right address?", 'email-not-found' ); } else if ( 'no_license' === $error->code ) { $error = $this->get_text_inline( "We can't see any active licenses associated with that email address, are you sure it's the right address?", 'no-active-licenses' ); } else { $error = $error->message; } } $licenses = array( 'success' => ( false === $error ) ); if ( false !== $error ) { $licenses['error'] = sprintf( '%s... %s', $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ), strtolower( $error ) ); } echo json_encode( $licenses ); exit; } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.8 * * @var string */ private static $_pagenow; /** * Get current page or the referer if executing a WP AJAX request. * * @author Vova Feldman (@svovaf) * @since 1.2.1.8 * * @return string */ static function get_current_page() { if ( ! isset( self::$_pagenow ) ) { global $pagenow; if ( empty( $pagenow ) && is_admin() && is_multisite() ) { /** * It appears that `$pagenow` is not yet initialized in some network admin pages when this method * is called, so initialize it here using some pieces of code from `wp-includes/vars.php`. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ if ( is_network_admin() ) { preg_match( '#/wp-admin/network/?(.*?)$#i', $_SERVER['PHP_SELF'], $self_matches ); } else if ( is_user_admin() ) { preg_match( '#/wp-admin/user/?(.*?)$#i', $_SERVER['PHP_SELF'], $self_matches ); } else { preg_match( '#/wp-admin/?(.*?)$#i', $_SERVER['PHP_SELF'], $self_matches ); } $pagenow = $self_matches[1]; $pagenow = trim( $pagenow, '/' ); $pagenow = preg_replace( '#\?.*?$#', '', $pagenow ); if ( '' === $pagenow || 'index' === $pagenow || 'index.php' === $pagenow ) { $pagenow = 'index.php'; } else { preg_match( '#(.*?)(/|$)#', $pagenow, $self_matches ); $pagenow = strtolower( $self_matches[1] ); if ( '.php' !== substr($pagenow, -4, 4) ) $pagenow .= '.php'; // for Options +Multiviews: /wp-admin/themes/index.php (themes.php is queried) } } self::$_pagenow = $pagenow; if ( self::is_ajax() && 'admin-ajax.php' === $pagenow ) { $referer = fs_get_raw_referer(); if ( is_string( $referer ) ) { $parts = explode( '?', $referer ); self::$_pagenow = basename( $parts[0] ); } } } return self::$_pagenow; } /** * Helper method to check if user in the plugins page. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @return bool */ static function is_plugins_page() { return ( 'plugins.php' === self::get_current_page() ); } /** * @author Leo Fajardo (@leorw) * @since 2.2.3 * * @return bool */ static function is_plugin_install_page() { return ( 'plugin-install.php' === self::get_current_page() ); } /** * @author Leo Fajardo (@leorw) * @since 2.0.2 * * @return bool */ static function is_updates_page() { return ( 'update-core.php' === self::get_current_page() ); } /** * Helper method to check if user in the themes page. * * @author Vova Feldman (@svovaf) * @since 1.2.2.6 * * @return bool */ static function is_themes_page() { return ( 'themes.php' === self::get_current_page() ); } #---------------------------------------------------------------------------------- #region Affiliation #---------------------------------------------------------------------------------- /** * @author Leo Fajardo (@leorw) * @since 1.2.3 * * @return bool */ function has_affiliate_program() { if ( ! is_object( $this->_plugin ) ) { return false; } return $this->_plugin->has_affiliate_program(); } /** * Get Plugin ID under which we will track affiliate application. * * This could either be the Bundle ID or the main plugin ID. * * @return number Bundle ID if developer has provided one, else the main plugin ID. */ private function get_plugin_id_for_affiliate_terms() { return $this->has_bundle_context() ? $this->get_bundle_id() : $this->_plugin->id; } /** * @author Leo Fajardo (@leorw) * @since 1.2.4 */ private function fetch_affiliate_terms() { if ( ! is_object( $this->plugin_affiliate_terms ) ) { /** * In case we have a bundle set in SDK configuration, we would like to use that for affiliates, not the main plugin. */ $plugins_api = $this->has_bundle_context() ? $this->get_api_bundle_scope() : $this->get_api_plugin_scope(); $affiliate_terms = $plugins_api->get( '/aff.json?type=affiliation', false ); /** * At this point, we intentionally don't fallback to the main plugin, because the developer has chosen to use bundle. So it makes sense the affiliate program should be in context to the bundle too. */ if ( ! $this->is_api_result_entity( $affiliate_terms ) ) { return; } $this->plugin_affiliate_terms = new FS_AffiliateTerms( $affiliate_terms ); } } /** * @author Leo Fajardo (@leorw) * @since 1.2.4 */ private function fetch_affiliate_and_custom_terms() { if ( ! empty( $this->_storage->affiliate_application_data ) ) { $application_data = $this->_storage->affiliate_application_data; $flush = ( ! isset( $application_data['status'] ) || 'pending' === $application_data['status'] ); $plugin_id_for_affiliate = $this->get_plugin_id_for_affiliate_terms(); $users_api = $this->get_api_user_scope(); $result = $users_api->get( "/plugins/{$plugin_id_for_affiliate}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json", $flush ); if ( $this->is_api_result_object( $result, 'affiliates' ) ) { if ( ! empty( $result->affiliates ) ) { $affiliate = new FS_Affiliate( $result->affiliates[0] ); if ( ! isset( $application_data['status'] ) || $application_data['status'] !== $affiliate->status ) { $application_data['status'] = $affiliate->status; $this->_storage->affiliate_application_data = $application_data; } if ( $affiliate->is_using_custom_terms ) { $affiliate_terms = $users_api->get( "/plugins/{$this->_plugin->id}/affiliates/{$affiliate->id}/aff/{$affiliate->custom_affiliate_terms_id}.json", $flush ); if ( $this->is_api_result_entity( $affiliate_terms ) ) { $this->custom_affiliate_terms = new FS_AffiliateTerms( $affiliate_terms ); } } $this->affiliate = $affiliate; } } } } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 */ private function fetch_affiliate_and_terms() { $this->_logger->entrance(); $this->fetch_affiliate_terms(); $this->fetch_affiliate_and_custom_terms(); } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 * * @return FS_Affiliate */ function get_affiliate() { return $this->affiliate; } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 * * @return FS_AffiliateTerms */ function get_affiliate_terms() { return is_object( $this->custom_affiliate_terms ) ? $this->custom_affiliate_terms : $this->plugin_affiliate_terms; } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 */ function _submit_affiliate_application() { $this->_logger->entrance(); $this->check_ajax_referer( 'submit_affiliate_application' ); if ( ! $this->is_user_admin() ) { // Only for admins. self::shoot_ajax_failure(); } $affiliate = fs_request_get( 'affiliate' ); if ( empty( $affiliate['promotion_methods'] ) ) { unset( $affiliate['promotion_methods'] ); } if ( ! empty( $affiliate['additional_domains'] ) ) { $affiliate['additional_domains'] = array_unique( $affiliate['additional_domains'] ); } if ( ! $this->is_registered() ) { $email_address = isset( $affiliate['email'] ) ? $affiliate['email'] : ''; if ( ! is_email( $email_address ) ) { self::shoot_ajax_failure('Invalid email address.'); } // Opt in but don't track usage. $next_page = $this->opt_in( $email_address, false, false, false, false, false, true ); if ( is_object( $next_page ) && $this->is_api_error( $next_page ) ) { self::shoot_ajax_failure( isset( $next_page->error ) ? $next_page->error->message : var_export( $next_page, true ) ); } else if ( $this->is_pending_activation() ) { self::shoot_ajax_failure( $this->get_text_inline( 'Account is pending activation. Please check your email and click the link to activate your account and then submit the affiliate form again.', 'account-is-pending-activation' ) ); } } $this->fetch_affiliate_terms(); $plugin_id_for_affiliate = $this->get_plugin_id_for_affiliate_terms(); $api = $this->get_api_user_scope(); $result = $api->call( ( "/plugins/{$plugin_id_for_affiliate}/aff/{$this->plugin_affiliate_terms->id}/affiliates.json" ), 'post', $affiliate ); if ( $this->is_api_error( $result ) ) { self::shoot_ajax_failure( isset( $result->error ) ? $result->error->message : var_export( $result, true ) ); } else { if ( $this->_admin_notices->has_sticky( 'affiliate_program' ) ) { $this->_admin_notices->remove_sticky( 'affiliate_program' ); } $affiliate_application_data = array( 'status' => 'pending', 'stats_description' => $affiliate['stats_description'], 'promotion_method_description' => $affiliate['promotion_method_description'], ); if ( ! empty( $affiliate['promotion_methods'] ) ) { $affiliate_application_data['promotion_methods'] = $affiliate['promotion_methods']; } if ( ! empty( $affiliate['domain'] ) ) { $affiliate_application_data['domain'] = $affiliate['domain']; } if ( ! empty( $affiliate['additional_domains'] ) ) { $affiliate_application_data['additional_domains'] = $affiliate['additional_domains']; } $this->_storage->affiliate_application_data = $affiliate_application_data; } // Purge cached affiliate. $api->purge_cache( 'affiliate.json' ); self::shoot_ajax_success( $result ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.3 * * @return array|null */ function get_affiliate_application_data() { if ( empty( $this->_storage->affiliate_application_data ) ) { return null; } return $this->_storage->affiliate_application_data; } #endregion Affiliation ------------------------------------------------------------ #---------------------------------------------------------------------------------- #region URL Generators #---------------------------------------------------------------------------------- /** * Alias to pricing_url(). * * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @uses pricing_url() * * @param string $period Billing cycle * @param bool $is_trial * * @return string */ function get_upgrade_url( $period = WP_FS__PERIOD_ANNUALLY, $is_trial = false ) { return $this->pricing_url( $period, $is_trial ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @uses get_upgrade_url() * * @return string */ function get_trial_url() { return $this->get_upgrade_url( WP_FS__PERIOD_ANNUALLY, true ); } /** * @author Leo Fajardo (@leorw) * @since 2.1.4 * * @param string $new_version * * @return string */ function version_upgrade_checkout_link( $new_version ) { if ( ! is_object( $this->_license ) ) { $url = $this->pricing_url(); $purchase_license_text = $this->get_text_inline( 'Buy a license now', 'buy-license-now' ); } else { $subscription = $this->_get_subscription( $this->_license->id ); $url = $this->checkout_url( is_object( $subscription ) ? ( 1 == $subscription->billing_cycle ? WP_FS__PERIOD_MONTHLY : WP_FS__PERIOD_ANNUALLY ) : WP_FS__PERIOD_LIFETIME, false, array( 'licenses' => $this->_license->quota ) ); $purchase_license_text = $this->get_text_inline( 'Renew your license now', 'renew-license-now' ); } return sprintf( $this->get_text_inline( '%s to access version %s security & feature updates, and support.', 'x-for-updates-and-support' ), sprintf( '%s', $this->apply_filters( 'update_notice_checkout_url', $url ), $purchase_license_text ), $new_version ); } /** * Plugin's pricing URL. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param string $billing_cycle Billing cycle * * @param bool $is_trial * * @return string */ function pricing_url( $billing_cycle = WP_FS__PERIOD_ANNUALLY, $is_trial = false ) { $this->_logger->entrance(); $params = array( 'billing_cycle' => $billing_cycle ); if ( $is_trial ) { $params['trial'] = 'true'; } $url = $this->is_addon() ? $this->_parent->addon_url( $this->_slug ) : $this->_get_admin_page_url( 'pricing', $params ); return $this->get_pricing_url_with_filter( $url ); } /** * Retrieves the filtered pricing URL. * * @author Leo Fajardo (@leorw) * @since 2.7.4 * * @param string $url * * @return string */ private function get_pricing_url_with_filter( $url ) { return $this->apply_filters( 'pricing_url', $url ); } /** * Checkout page URL. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param string $billing_cycle Billing cycle * @param bool $is_trial * @param array $extra (optional) Extra parameters, override other query params. * @param bool|null $network * * @return string */ function checkout_url( $billing_cycle = WP_FS__PERIOD_ANNUALLY, $is_trial = false, $extra = array(), $network = null ) { $this->_logger->entrance(); $params = array( 'checkout' => 'true', 'billing_cycle' => $billing_cycle, ); if ( $is_trial ) { $params['trial'] = 'true'; } /** * Params in extra override other params. */ $params = array_merge( $params, $extra ); return $this->apply_filters( 'checkout_url', $this->_get_admin_page_url( 'pricing', $params, $network ) ); } /** * Add-on checkout URL. * * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @param number $addon_id * @param number $pricing_id * @param string $billing_cycle * @param bool $is_trial * @param bool|null $network * * @return string */ function addon_checkout_url( $addon_id, $pricing_id, $billing_cycle = WP_FS__PERIOD_ANNUALLY, $is_trial = false, $network = null ) { return $this->checkout_url( $billing_cycle, $is_trial, array( 'plugin_id' => $addon_id, 'pricing_id' => $pricing_id, ), $network ); } #endregion #endregion ------------------------------------------------------------------ /** * Check if plugin has any add-ons. * * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @since 1.1.7.3 Base logic only on the parameter provided by the developer in the init function. * * @return bool */ function has_addons() { $this->_logger->entrance(); return $this->_has_addons; } /** * Check if plugin can work in anonymous mode. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool * * @deprecated Please use is_enable_anonymous() instead. */ function enable_anonymous() { return $this->_enable_anonymous; } /** * Check if plugin can work in anonymous mode. * * @author Vova Feldman (@svovaf) * @since 1.1.9 * * @return bool */ function is_enable_anonymous() { return $this->_enable_anonymous; } /** * Check if plugin is premium only (no free plans). * * @author Vova Feldman (@svovaf) * @since 1.1.9 * * @return bool */ function is_only_premium() { return $this->_is_premium_only; } /** * Checks if the plugin's type is "plugin". The other type is "theme". * * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return bool */ function is_plugin() { return ( WP_FS__MODULE_TYPE_PLUGIN === $this->_module_type ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return string */ function get_module_type() { if ( ! isset( $this->_module_type ) ) { $id_slug_type_path_map = self::$_accounts->get_option( 'id_slug_type_path_map', array() ); $this->_module_type = $id_slug_type_path_map[ $this->_module_id ]['type']; } return $this->_module_type; } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return string */ function get_plugin_main_file_path() { return $this->_plugin_main_file_path; } /** * Check if module has a premium code version. * * Serviceware module might be freemium without any * premium code version, where the paid features * are all part of the service. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @return bool */ function has_premium_version() { return $this->_has_premium_version; } /** * Check if feature supported with current site's plan. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @todo IMPLEMENT * * @param number $feature_id * * @throws Exception */ function is_feature_supported( $feature_id ) { throw new Exception( 'not implemented' ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @return bool Is running in SSL/HTTPS */ function is_ssl() { return WP_FS__IS_HTTPS; } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool Is running in AJAX call. * * @link http://wordpress.stackexchange.com/questions/70676/how-to-check-if-i-am-in-admin-ajax */ static function is_ajax() { return ( defined( 'DOING_AJAX' ) && DOING_AJAX ); } /** * Check if it's an AJAX call targeted for the current module. * * @author Vova Feldman (@svovaf) * @since 1.2.0 * * @param array|string $actions Collection of AJAX actions. * * @return bool */ function is_ajax_action( $actions ) { // Verify it's an ajax call. if ( ! self::is_ajax() ) { return false; } // Verify the call is relevant for the plugin. if ( $this->_module_id != fs_request_get( 'module_id' ) ) { return false; } // Verify it's one of the specified actions. if ( is_string( $actions ) ) { $actions = explode( ',', $actions ); } if ( is_array( $actions ) && 0 < count( $actions ) ) { $ajax_action = fs_request_get( 'action' ); foreach ( $actions as $action ) { if ( $ajax_action === $this->get_action_tag( $action ) ) { return true; } } } return false; } /** * Check if it's an AJAX call targeted for current request. * * @author Vova Feldman (@svovaf) * @since 1.2.0 * * @param array|string $actions Collection of AJAX actions. * @param number|null $module_id * * @return bool */ static function is_ajax_action_static( $actions, $module_id = null ) { // Verify it's an ajax call. if ( ! self::is_ajax() ) { return false; } if ( ! empty( $module_id ) ) { // Verify the call is relevant for the plugin. if ( $module_id != fs_request_get( 'module_id' ) ) { return false; } } // Verify it's one of the specified actions. if ( is_string( $actions ) ) { $actions = explode( ',', $actions ); } if ( is_array( $actions ) && 0 < count( $actions ) ) { $ajax_action = fs_request_get( 'action' ); foreach ( $actions as $action ) { if ( $ajax_action === self::get_ajax_action_static( $action, $module_id ) ) { return true; } } } return false; } /** * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @return bool */ static function is_cron() { return ( defined( 'DOING_CRON' ) && DOING_CRON ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @return bool */ static function is_admin_post() { return ( 'admin-post.php' === self::get_current_page() ); } /** * Check if a real user is visiting the admin dashboard. * * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @return bool */ function is_user_in_admin() { return ( is_admin() && ! self::is_ajax() && ! self::is_cron() && ! self::is_admin_post() ); } /** * Check if a real user is in the customizer view. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool */ static function is_customizer() { return is_customize_preview(); } /** * Check if running in HTTPS and if site's plan matching the specified plan. * * @param string $plan * @param bool $exact * * @return bool */ function is_ssl_and_plan( $plan, $exact = false ) { return ( $this->is_ssl() && $this->is_plan( $plan, $exact ) ); } /** * Construct plugin's settings page URL. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param string $page * @param array $params * @param bool|null $network * * @return string */ function _get_admin_page_url( $page = '', $params = array(), $network = null ) { if ( is_null( $network ) ) { $network = ( $this->_is_network_active && ( fs_is_network_admin() || ! $this->is_delegated_connection() ) ); } if ( 0 < count( $params ) ) { foreach ( $params as $k => $v ) { $params[ $k ] = urlencode( $v ); } } $page_param = $this->_menu->get_slug( $page ); if ( empty( $page ) && // Show the opt-in as an overlay for free wp.org themes or themes without any settings page. $this->show_opt_in_on_themes_page() ) { $params[ $this->get_unique_affix() . '_show_optin' ] = 'true'; return add_query_arg( $params, $this->admin_url( 'themes.php', 'admin', $network ) ); } if ( ! $this->has_settings_menu() ) { if ( ! empty( $page ) ) { // Module doesn't have a setting page, but since the request is for // a specific Freemius page, use the admin.php path. return add_query_arg( array_merge( $params, array( 'page' => $page_param, ) ), $this->admin_url( 'admin.php', 'admin', $network ) ); } else { if ( $this->is_activation_mode() ) { /** * @author Vova Feldman * @since 1.2.1.6 * * If plugin doesn't have a settings page, create one for the opt-in screen. */ return add_query_arg( array_merge( $params, array( 'page' => $this->_slug, ) ), $this->admin_url( 'admin.php', 'admin', $network ) ); } else { // Plugin without a settings page. return add_query_arg( $params, $this->admin_url( 'plugins.php', 'admin', $network ) ); } } } // Module has a submenu settings page. if ( ! $this->_menu->is_top_level() ) { $parent_slug = $this->_menu->get_parent_slug(); $menu_file = ( false !== strpos( $parent_slug, '.php' ) ) ? $parent_slug : 'admin.php'; return add_query_arg( array_merge( $params, array( 'page' => $page_param, ) ), $this->admin_url( $menu_file, 'admin', $network ) ); } // Module has a top level CPT settings page. if ( $this->_menu->is_cpt() ) { if ( empty( $page ) && $this->is_activation_mode() ) { return add_query_arg( array_merge( $params, array( 'page' => $page_param ) ), $this->admin_url( 'admin.php', 'admin', $network ) ); } else { if ( ! empty( $page ) ) { $params['page'] = $page_param; } return add_query_arg( $params, $this->admin_url( $this->_menu->get_raw_slug(), 'admin', $network ) ); } } // Module has a custom top level settings page. return add_query_arg( array_merge( $params, array( 'page' => $page_param, ) ), $this->admin_url( 'admin.php', 'admin', $network ) ); } #-------------------------------------------------------------------------------- #region Multisite #-------------------------------------------------------------------------------- /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @return bool */ function is_network_active() { return $this->_is_network_active; } /** * Delegate activation for the given sites in the network (or all sites if `null`) to site admins. * * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param bool|int[] $all_or_blog_ids */ private function delegate_connection( $all_or_blog_ids = true ) { $this->_logger->entrance(); $this->_admin_notices->remove_sticky( 'connect_account' ); if ( true === $all_or_blog_ids ) { // All sites delegation. $this->_storage->store( 'is_delegated_connection', true, true ); } else { // Specified sites delegation. foreach ( $all_or_blog_ids as $blog_id ) { $this->delegate_site_connection( $blog_id ); } } $this->network_upgrade_mode_completed(); } /** * Delegate specific network site conncetion to the site admin. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id */ private function delegate_site_connection( $blog_id ) { $this->_storage->store( 'is_delegated_connection', true, $blog_id ); } /** * Check if super-admin delegated the connection of ALL sites to the site admins. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return bool */ function is_network_delegated_connection() { if ( ! $this->_is_network_active ) { return false; } return $this->_storage->get( 'is_delegated_connection', false, true ); } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param int $blog_id * * @return bool */ function is_site_delegated_connection( $blog_id = 0 ) { if ( ! $this->_is_network_active ) { return false; } if ( 0 == $blog_id ) { $blog_id = get_current_blog_id(); } return $this->_storage->get( 'is_delegated_connection', false, $blog_id ); } /** * Check if delegated the connection. When running within the network admin, * and haven't specified the blog ID, checks if network level delegated. If running * within a site admin or specified a blog ID, check if delegated the connection for * the current context site. * * If executed outside the the admin, check if delegated the connection * for the current context site OR the whole network. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id If set, checks if network delegated or blog specific delegated. * * @return bool */ function is_delegated_connection( $blog_id = 0 ) { if ( ! $this->_is_network_active ) { return false; } if ( fs_is_network_admin() && 0 == $blog_id ) { return $this->is_network_delegated_connection(); } return ( $this->is_network_delegated_connection() || $this->is_site_delegated_connection( $blog_id ) ); } /** * Check if the current module is active for the site. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id * * @return bool */ function is_active_for_site( $blog_id ) { if ( ! is_multisite() ) { // Not a multisite and this code is executed, means that the plugin is active. return true; } if ( $this->is_theme() ) { // All themes are site level activated. return true; } if ( $this->_is_network_active ) { // Plugin was network activated so it's active. return true; } return in_array( $this->_plugin_basename, (array) get_blog_option( $blog_id, 'active_plugins', array() ) ); } /** * @todo Implement pagination when accessing the subsites collection. * * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param int $limit Default to 1,000 * @param int $offset Default to 0 * * @return array Active & public sites collection. */ static function get_sites( $limit = 1000, $offset = 0 ) { if ( ! is_multisite() ) { return array(); } /** * For consistency with get_blog_list() which only return active public sites. * * @author Vova Feldman (@svovaf) */ $args = array( /** * Commented out in order to handle the migration of site options whether the site is public or not. * * @author Leo Fajardo (@leorw) * @since 2.2.1 */ // 'public' => 1, 'archived' => 0, 'mature' => 0, 'spam' => 0, 'deleted' => 0, 'number' => $limit, 'offset' => $offset, ); return get_sites( $args ); } /** * Checks if a given blog is active. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param $blog_id * * @return bool */ private static function is_site_active( $blog_id ) { global $wpdb; $blog_info = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->blogs} WHERE blog_id = %d", $blog_id ) ); if ( ! is_object( $blog_info ) ) { return false; } return ( true == $blog_info->public && false == $blog_info->archived && false == $blog_info->mature && false == $blog_info->spam && false == $blog_info->deleted ); } /** * Get a mapping between the site addresses to their blog IDs. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return array { * @key string Site address without protocol with a trailing slash. * @value int Site's blog ID. * } */ private function get_address_to_blog_map() { $sites = self::get_sites(); // Map site addresses to their blog IDs. $address_to_blog_map = array(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $address = self::get_unfiltered_site_url( $blog_id, true, true ); $address_to_blog_map[ $address ] = $blog_id; } return $address_to_blog_map; } /** * Get a mapping between the site addresses to their blog IDs. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return array { * @key int Site's blog ID. * @value FS_Site Associated install. * } */ function get_blog_install_map() { $sites = self::get_sites(); // Map site blog ID to its install. $install_map = array(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $install = $this->get_install_by_blog_id( $blog_id ); if ( is_object( $install ) ) { $install_map[ $blog_id ] = $install; } } return $install_map; } /** * @author Vova Feldman (@svovaf) * @since 2.5.1 * * @param bool|null $is_delegated When `true`, returns only connection delegated blog IDs. When `false`, only non-delegated blog IDs. * * @return int[] */ private function get_blog_ids( $is_delegated = null ) { $blog_ids = array(); $sites = self::get_sites(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); if ( is_null( $is_delegated ) || $is_delegated === $this->is_site_delegated_connection( $blog_id ) ) { $blog_ids[] = $blog_id; } } return $blog_ids; } /** * @author Vova Feldman (@svovaf) * @since 2.5.1 * * @return int[] */ private function get_non_delegated_blog_ids() { return $this->get_blog_ids( false ); } /** * Gets a map of module IDs that the given user has opted-in to. * * @author Leo Fajardo (@leorw) * @since 2.1.0 * * @param number $fs_user_id * * @return array { * @key number $plugin_id * @value bool Always true. * } */ private static function get_user_opted_in_module_ids_map( $fs_user_id ) { self::$_static_logger->entrance(); if ( ! is_multisite() ) { $installs = array_merge( self::get_all_sites( WP_FS__MODULE_TYPE_PLUGIN ), self::get_all_sites( WP_FS__MODULE_TYPE_THEME ) ); } else { $sites = self::get_sites(); $installs = array(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $installs = array_merge( $installs, self::get_all_sites( WP_FS__MODULE_TYPE_PLUGIN, $blog_id ), self::get_all_sites( WP_FS__MODULE_TYPE_THEME, $blog_id ) ); } } $module_ids_map = array(); foreach ( $installs as $install ) { if ( is_object( $install ) && FS_Site::is_valid_id( $install->id ) && FS_User::is_valid_id( $install->user_id ) && ( $install->user_id == $fs_user_id ) ) { $module_ids_map[ $install->plugin_id ] = true; } } return $module_ids_map; } /** * @author Leo Fajardo (@leorw) * * @return null|array { * 'install' => FS_Site Module's install, * 'blog_id' => string The associated blog ID. * } */ function find_first_install() { $sites = self::get_sites(); foreach ( $sites as $site ) { $blog_id = self::get_site_blog_id( $site ); $install = $this->get_install_by_blog_id( $blog_id ); if ( is_object( $install ) ) { return array( 'install' => $install, 'blog_id' => $blog_id ); } } return null; } /** * Switches the Freemius site level context to a specified blog. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $blog_id * @param FS_Site $install * @param bool $flush * * @return bool Since 2.3.1 returns if a switch was made. */ function switch_to_blog( $blog_id, FS_Site $install = null, $flush = false ) { if ( ! is_numeric( $blog_id ) ) { return false; } if ( ! $flush && $blog_id == $this->_context_is_network_or_blog_id ) { return false; } switch_to_blog( $blog_id ); $this->_context_is_network_or_blog_id = $blog_id; self::$_accounts->set_site_blog_context( $blog_id ); $this->_storage->set_site_blog_context( $blog_id ); $this->_storage->set_network_active( $this->_is_network_active, $this->is_delegated_connection( $blog_id ) ); $this->_site = is_object( $install ) ? $install : $this->get_install_by_blog_id( $blog_id ); $this->_user = false; $this->_licenses = false; $this->_license = null; $this->is_whitelabeled = null; if ( is_object( $this->_site ) ) { // Try to fetch user from install. $this->_user = self::_get_user_by_id( $this->_site->user_id ); if ( ! is_object( $this->_user ) && FS_User::is_valid_id( $this->_storage->prev_user_id ) ) { // Try to fetch previously saved user. $this->_user = self::_get_user_by_id( $this->_storage->prev_user_id ); if ( ! is_object( $this->_user ) ) { // Fallback to network's user. $this->_user = $this->get_network_user(); } } $all_plugin_licenses = self::get_all_licenses( $this->_module_id ); if ( ! empty( $all_plugin_licenses ) ) { if ( ! FS_Plugin_License::is_valid_id( $this->_site->license_id ) ) { $this->_license = null; } else { $license_found = false; foreach ( $all_plugin_licenses as $license ) { if ( $license->id == $this->_site->license_id ) { // License found. $this->_license = $license; $license_found = true; break; } } if ( $license_found ) { $this->link_license_2_user( $this->_license->id, $this->_user->id ); } } $this->_licenses = $this->get_user_licenses( $this->_user->id ); } } unset( $this->_site_api ); unset( $this->_user_api ); return true; } /** * Restore the blog context to the blog that originally loaded the module. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ function restore_current_blog() { $this->switch_to_blog( $this->_blog_id ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param array|WP_Site $site * * @return int */ static function get_site_blog_id( &$site ) { return ( $site instanceof WP_Site ) ? $site->blog_id : ( is_object( $site ) && isset( $site->userblog_id ) ? $site->userblog_id : $site['blog_id'] ); } /** * @author Vova Feldman (@svovaf) * @since 2.5.1 * * @param WP_Site[]|array[] $sites * * @return int[] */ static function get_sites_blog_ids( $sites ) { $blog_ids = array(); foreach ( $sites as $site ) { $blog_ids[] = self::get_site_blog_id( $site ); } return $blog_ids; } /** * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param array|WP_Site|null $site * @param bool $load_registration Since 2.5.1 When set to `true` the method will attempt to return the subsite's registration date, regardless of the `$site` type and value. In most calls, the registration date will be returned anyway, even when the value is `false`. This param is purely for performance optimization. * * @return array */ function get_site_info( $site = null, $load_registration = false ) { $this->_logger->entrance(); $switched = false; $registration_date = null; if ( is_null( $site ) ) { $url = self::get_unfiltered_site_url(); $name = get_bloginfo( 'name' ); $blog_id = null; } else { $blog_id = self::get_site_blog_id( $site ); if ( get_current_blog_id() != $blog_id ) { switch_to_blog( $blog_id ); $switched = true; } if ( $site instanceof WP_Site ) { $url = $site->siteurl; $name = $site->blogname; $registration_date = $site->registered; } else { $url = self::get_unfiltered_site_url( $blog_id ); $name = get_bloginfo( 'name' ); } } if ( empty( $registration_date ) && $load_registration ) { $blog_details = get_blog_details( $blog_id, false ); if ( is_object( $blog_details ) && isset( $blog_details->registered ) ) { $registration_date = $blog_details->registered; } } $info = array( 'uid' => $this->get_anonymous_id( $blog_id ), 'url' => $url, ); // Add these diagnostic information only if user allowed to track. if ( FS_Permission_Manager::instance( $this )->is_diagnostic_tracking_allowed() ) { $info = array_merge( $info, array( 'title' => $name, 'language' => self::get_sanitized_language(), ) ); } if ( is_numeric( $blog_id ) ) { $info['blog_id'] = $blog_id; } if ( ! empty( $registration_date ) ) { $info[ 'registration_date' ] = $registration_date; } if ( $switched ) { restore_current_blog(); } return $info; } /** * Load the module's install based on the blog ID. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int|null $blog_id * * @return FS_Site */ function get_install_by_blog_id( $blog_id = null ) { $installs = self::get_all_sites( $this->_module_type, $blog_id ); $install = isset( $installs[ $this->_slug ] ) ? $installs[ $this->_slug ] : null; if ( is_object( $install ) && is_numeric( $install->id ) && is_numeric( $install->user_id ) && FS_Plugin_Plan::is_valid_id( $install->plan_id ) ) { // Load site. $install = clone $install; } return $install; } /** * Check if module is installed on a specified site. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int|null $blog_id * * @return bool */ function is_installed_on_site( $blog_id = null ) { $installs = self::get_all_sites( $this->_module_type, $blog_id ); $install = isset( $installs[ $this->_slug ] ) ? $installs[ $this->_slug ] : null; return ( is_object( $install ) && is_numeric( $install->id ) && is_numeric( $install->user_id ) && FS_Plugin_Plan::is_valid_id( $install->plan_id ) ); } /** * Check if super-admin connected at least one site via the network opt-in. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return bool */ function is_network_registered() { if ( ! $this->_is_network_active ) { return false; } return FS_User::is_valid_id( $this->_storage->network_user_id ); } /** * Returns the main user associated with the network. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return FS_User */ function get_network_user() { if ( ! $this->_is_network_active ) { return null; } return FS_User::is_valid_id( $this->_storage->network_user_id ) ? self::_get_user_by_id( $this->_storage->network_user_id ) : null; } /** * Returns the current context user or the network's main user. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return FS_User */ function get_current_or_network_user() { return ( $this->_user instanceof FS_User ) ? $this->_user : $this->get_network_user(); } /** * Returns the main install associated with the network. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return FS_Site */ function get_network_install() { if ( ! $this->_is_network_active ) { return null; } return FS_Site::is_valid_id( $this->_storage->network_install_blog_id ) ? $this->get_install_by_blog_id( $this->_storage->network_install_blog_id ) : null; } /** * Returns the blog ID that is associated with the main install. * * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @return int|null */ function get_network_install_blog_id() { if ( ! $this->_is_network_active ) { return null; } return FS_Site::is_valid_id( $this->_storage->network_install_blog_id ) ? $this->_storage->network_install_blog_id : null; } /** * Returns the current context install or the network's main install. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return FS_Site */ function get_current_or_network_install() { return ( $this->_site instanceof FS_Site ) ? $this->_site : $this->get_network_install(); } /** * Check if executing a site level action from the network level admin. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return false|int If yes, return the requested blog ID. */ private function is_network_level_site_specific_action() { if ( ! $this->_is_network_active ) { return false; } if ( ! fs_is_network_admin() ) { return false; } $blog_id = fs_request_get( 'blog_id', '' ); return is_numeric( $blog_id ) ? $blog_id : false; } /** * Check if executing an action from the network level admin. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return bool */ private function is_network_level_action() { return ( $this->_is_network_active && fs_is_network_admin() ); } /** * Needs to be executed after site deactivation, archive, deletion, or flag as spam. * The logic updates the network level user and blog, and reschedule the crons if the cron executing site matching the site that is no longer publicly active. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $context_blog_id */ private function update_multisite_data_after_site_deactivation( $context_blog_id = 0 ) { $this->_logger->entrance(); if ( $this->_is_network_active ) { if ( $context_blog_id == $this->_storage->network_install_blog_id ) { $installs_map = $this->get_blog_install_map(); foreach ( $installs_map as $blog_id => $install ) { /** * @var FS_Site $install */ if ( $context_blog_id == $blog_id ) { continue; } if ( $install->user_id != $this->_storage->network_user_id ) { continue; } // Switch reference to a blog that is opted-in and belong to the same super-admin. $this->_storage->network_install_blog_id = $blog_id; break; } } } if ( ! $this->is_registered() ) { return; } if ( $this->is_sync_cron_scheduled() && $context_blog_id == $this->get_sync_cron_blog_id() ) { $this->schedule_sync_cron( WP_FS__SCRIPT_START_TIME, true, $context_blog_id ); } if ( $this->is_install_sync_scheduled() && $context_blog_id == $this->get_install_sync_cron_blog_id() ) { $this->maybe_schedule_install_sync_cron( $context_blog_id ); } } /** * Executed after site deactivation, archive, or flag as spam. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $context_blog_id */ public function _after_site_deactivated_callback( $context_blog_id = 0 ) { $this->_logger->entrance(); $install = $this->get_install_by_blog_id( $context_blog_id ); if ( ! is_object( $install ) ) { // Site not connected. return; } $this->update_multisite_data_after_site_deactivation( $context_blog_id ); if ( ! $this->is_registered() ) { return; } $current_blog_id = get_current_blog_id(); $this->switch_to_blog( $context_blog_id ); // Send deactivation event. $this->sync_install( array( 'is_active' => false, ) ); $this->switch_to_blog( $current_blog_id ); } /** * Executed after site deletion. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $context_blog_id * @param bool $drop True if site's database tables should be dropped. Default is false. */ public function _after_site_deleted_callback( $context_blog_id = 0, $drop = false ) { $this->_logger->entrance(); $install = $this->get_install_by_blog_id( $context_blog_id ); if ( ! is_object( $install ) ) { // Site not connected. return; } $this->update_multisite_data_after_site_deactivation( $context_blog_id ); if ( ! $this->is_registered() ) { return; } $current_blog_id = get_current_blog_id(); $this->switch_to_blog( $context_blog_id ); if ( $drop ) { // Delete install if dropping site DB. $this->delete_account_event(); } else { // Send deactivation event. $this->sync_install( array( 'is_active' => false, ) ); } $this->switch_to_blog( $current_blog_id ); } /** * Executed after site deletion, called from wp_delete_site * * @author Dario Curvino (@dudo) * @since 2.5.0 * * @param WP_Site $old_site */ public function _after_wpsite_deleted_callback( WP_Site $old_site ) { $this->_logger->entrance(); $this->_after_site_deleted_callback( $old_site->blog_id, true ); } /** * Executed after site re-activation. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param int $context_blog_id */ public function _after_site_reactivated_callback( $context_blog_id = 0 ) { $this->_logger->entrance(); $install = $this->get_install_by_blog_id( $context_blog_id ); if ( ! is_object( $install ) ) { // Site not connected. return; } if ( ! self::is_site_active( $context_blog_id ) ) { // Site not yet active (can be in spam mode, archived, deleted...). return; } $current_blog_id = get_current_blog_id(); $this->switch_to_blog( $context_blog_id ); // Send re-activation event. $this->sync_install( array( 'is_active' => true, ) ); $this->switch_to_blog( $current_blog_id ); } #endregion Multisite /** * @author Leo Fajardo (@leorw) * * @param string $path * @param string $scheme * @param bool $network * * @return string */ private function admin_url( $path = '', $scheme = 'admin', $network = true ) { return ( $this->_is_network_active && $network ) ? network_admin_url( $path, $scheme ) : admin_url( $path, $scheme ); } /** * Check if currently in a specified admin page. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @param string $page * * @return bool */ function is_admin_page( $page ) { return ( $this->_menu->get_slug( $page ) === fs_request_get( 'page', '', 'get' ) ); } /** * Check if currently in the product's main admin page. * * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @return bool */ function is_main_admin_page() { return $this->is_admin_page( '' ); } /** * Get module's main admin setting page URL. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return string */ function main_menu_url() { return $this->_menu->main_menu_url(); } /** * Check if currently on the theme's setting page or * on any of the Freemius added pages (via tabs). * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool * * @deprecated Please use is_product_settings_page() instead; */ function is_theme_settings_page() { return $this->is_product_settings_page(); } /** * Check if currently on the product's main setting page or on any of the Freemius added pages (via tabs). * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool */ function is_product_settings_page() { $page = fs_request_get( 'page', '', 'get' ); $menu_slug = $this->_menu->get_slug(); if ( $page === $menu_slug ) { return true; } return fs_starts_with( // e.g., {$menu_slug}-account, {$menu_slug}-affiliation, etc. $page, ( $menu_slug . '-' ) ); } /** * Plugin's account page + sync license URL. * * @author Vova Feldman (@svovaf) * @since 1.1.9.1 * * @param bool|number $plugin_id * @param bool $add_action_nonce * @param array $params * * @return string */ function _get_sync_license_url( $plugin_id = false, $add_action_nonce = true, $params = array() ) { if ( is_numeric( $plugin_id ) ) { $params['plugin_id'] = $plugin_id; } return $this->get_account_url( $this->get_unique_affix() . '_sync_license', $params, $add_action_nonce ); } /** * Plugin's account URL. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param bool|string $action * @param array $params * * @param bool $add_action_nonce * * @return string */ function get_account_url( $action = false, $params = array(), $add_action_nonce = true ) { if ( is_string( $action ) ) { $params['fs_action'] = $action; } self::require_pluggable_essentials(); return ( $add_action_nonce && is_string( $action ) ) ? fs_nonce_url( $this->_get_admin_page_url( 'account', $params ), $action ) : $this->_get_admin_page_url( 'account', $params ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.0 * * @param string $tab * @param bool $action * @param array $params * @param bool $add_action_nonce * * @return string * * @uses get_account_url() */ function get_account_tab_url( $tab, $action = false, $params = array(), $add_action_nonce = true ) { $params['tab'] = $tab; return $this->get_account_url( $action, $params, $add_action_nonce ); } /** * Plugin's account URL. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param bool|string $topic * @param bool|string $message * @param bool|string $summary Since 2.5.1. * * @return string */ function contact_url( $topic = false, $message = false, $summary = false ) { $params = array(); if ( is_string( $topic ) ) { $params['topic'] = $topic; } if ( is_string( $message ) ) { $params['message'] = $message; } if ( is_string( $summary ) ) { $params['summary'] = $summary; } if ( $this->is_addon() ) { $params['addon_id'] = $this->get_id(); return $this->get_parent_instance()->_get_admin_page_url( 'contact', $params ); } else { return $this->_get_admin_page_url( 'contact', $params ); } } /** * Add-on direct info URL. * * @author Vova Feldman (@svovaf) * @since 1.1.0 * * @param string $slug * * @return string */ function addon_url( $slug ) { return $this->_get_admin_page_url( 'addons', array( 'slug' => $slug ) ); } /** * Add-ons URL. * * @author Vova Feldman (@svovaf) * @since 2.4.5 * * @return string */ function get_addons_url() { return $this->_get_admin_page_url( 'addons' ); } /* Logger ------------------------------------------------------------------------------------------------------------------*/ /** * @param string $id * @param bool $prefix_slug * * @return FS_Logger */ function get_logger( $id = '', $prefix_slug = true ) { return FS_Logger::get_logger( ( $prefix_slug ? $this->_slug : '' ) . ( ( ! $prefix_slug || empty( $id ) ) ? '' : '_' ) . $id ); } /** * Note: This method is used externally so don't delete it. * * @param $id * @param bool $load_options * @param bool $prefix_slug * * @return FS_Option_Manager */ function get_options_manager( $id, $load_options = false, $prefix_slug = true ) { return FS_Option_Manager::get_manager( ( $prefix_slug ? $this->_slug : '' ) . ( ( ! $prefix_slug || empty( $id ) ) ? '' : '_' ) . $id, $load_options ); } /* Security ------------------------------------------------------------------------------------------------------------------*/ private static function _encrypt( $str ) { if ( is_null( $str ) ) { return null; } /** * The encrypt/decrypt functions are used to protect * the user from messing up with some of the sensitive * data stored for the module as a JSON in the database. * * I used the same suggested hack by the theme review team. * For more details, look at the function `Base64UrlDecode()` * in `./sdk/FreemiusBase.php`. * * @todo Remove this hack once the base64 error is removed from the Theme Check. * * @author Vova Feldman (@svovaf) * @since 1.2.2 */ $fn = 'base64' . '_encode'; return $fn( $str ); } static function _decrypt( $str ) { if ( is_null( $str ) ) { return null; } /** * The encrypt/decrypt functions are used to protect * the user from messing up with some of the sensitive * data stored for the module as a JSON in the database. * * I used the same suggested hack by the theme review team. * For more details, look at the function `Base64UrlDecode()` * in `./sdk/FreemiusBase.php`. * * @todo Remove this hack once the base64 error is removed from the Theme Check. * * @author Vova Feldman (@svovaf) * @since 1.2.2 */ $fn = 'base64' . '_decode'; return $fn( $str ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param FS_Entity $entity * * @return FS_Entity Return an encrypted clone entity. */ private static function _encrypt_entity( FS_Entity $entity ) { $clone = clone $entity; $props = get_object_vars( $entity ); foreach ( $props as $key => $val ) { $clone->{$key} = self::_encrypt( $val ); } return $clone; } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param FS_Entity $entity * * @return FS_Entity Return an decrypted clone entity. */ private static function decrypt_entity( FS_Entity $entity ) { $clone = clone $entity; $props = get_object_vars( $entity ); foreach ( $props as $key => $val ) { $clone->{$key} = self::_decrypt( $val ); } return $clone; } /** * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @param string $email * * @return FS_User|false */ public static function _get_user_by_email( $email ) { self::$_static_logger->entrance(); $email = trim( strtolower( $email ) ); $users = self::get_all_users(); if ( is_array( $users ) ) { foreach ( $users as $user ) { if ( $email === trim( strtolower( $user->email ) ) ) { return $user; } } } return false; } #---------------------------------------------------------------------------------- #region Account (Loading, Updates & Activation) #---------------------------------------------------------------------------------- /*** * Load account information (user + site). * * @author Vova Feldman (@svovaf) * @since 1.0.1 */ private function _load_account() { $this->_logger->entrance(); $this->do_action( 'before_account_load' ); $users = self::get_all_users(); $plans = self::get_all_plans( $this->_module_type ); if ( $this->_logger->is_on() && is_admin() ) { $this->_logger->log( 'users = ' . var_export( $users, true ) ); $this->_logger->log( 'plans = ' . var_export( $plans, true ) ); } $site = fs_is_network_admin() ? $this->get_network_install() : $this->get_install_by_blog_id(); if ( fs_is_network_admin() && $this->is_network_active() && ! is_object( $site ) && FS_Site::is_valid_id( $this->_storage->network_install_blog_id ) ) { $first_install = $this->find_first_install(); if ( is_null( $first_install ) ) { unset( $this->_storage->network_install_blog_id ); } else { $site = $first_install['install']; $this->_storage->network_install_blog_id = $first_install['blog_id']; } } if ( is_object( $site ) && is_numeric( $site->id ) && is_numeric( $site->user_id ) && FS_Plugin_Plan::is_valid_id( $site->plan_id ) ) { // Load site. $this->_site = $site; } $user = null; if ( fs_is_network_admin() && $this->_is_network_active ) { $user = $this->get_network_user(); } if ( is_object( $user ) ) { $this->_user = clone $user; } else if ( $this->_site ) { $user = self::_get_user_by_id( $this->_site->user_id ); if ( ! is_object( $user ) && FS_User::is_valid_id( $this->_storage->prev_user_id ) ) { /** * Try to load the previous owner. This recovery is used for the following use-case: * 1. Opt-in * 2. Cloning site1 to site2 * 3. Ownership switch in site1 (same applies for site2) * 4. Install data sync on site2 * 5. Now site2's install is associated with the new owner which does not exists locally. */ $user = self::_get_user_by_id( $this->_storage->prev_user_id ); } if ( ! is_object( $user ) ) { /** * This is a special fault tolerance mechanism to handle a scenario that the user data is missing. */ if ( ! isset( $this->_storage->user_recovery_from_install_last_attempt_timestamp ) || time() > ( $this->_storage->user_recovery_from_install_last_attempt_timestamp + FS_Clone_Manager::CLONE_RESOLUTION_MAX_EXECUTION_TIME ) ) { $user = $this->sync_user_by_current_install(); } else { return; } if ( is_object( $user ) ) { $this->_storage->user_was_recovered_from_install = true; } else { $this->_storage->user_recovery_from_install_attempts = isset( $this->_storage->user_recovery_from_install_attempts ) ? ( $this->_storage->user_recovery_from_install_attempts + 1 ) : 1; if ( $this->_storage->user_recovery_from_install_attempts >= 3 ) { $this->delete_current_install( false ); } else { $this->_storage->user_recovery_from_install_last_attempt_timestamp = time(); return; } } } $this->_user = ( $user instanceof FS_User ) ? clone $user : null; } if ( is_object( $this->_user ) ) { // Load licenses. $this->_licenses = $this->get_user_licenses( $this->_user->id ); } if ( is_object( $this->_site ) ) { // Load plans. $this->_plans = isset( $plans[ $this->_slug ] ) ? $plans[ $this->_slug ] : array(); if ( ! is_array( $this->_plans ) || empty( $this->_plans ) ) { $this->_sync_plans(); } else { for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) { if ( $this->_plans[ $i ] instanceof FS_Plugin_Plan ) { $this->_plans[ $i ] = self::decrypt_entity( $this->_plans[ $i ] ); } else { unset( $this->_plans[ $i ] ); } } } $this->_license = $this->_get_license_by_id( $this->_site->license_id ); if ( $this->_site->version != $this->get_plugin_version() ) { // If stored install version is different than current installed plugin version, // then update plugin version event. $this->update_plugin_version_event(); } } if ( true === $this->_storage->require_license_activation && ! fs_request_get_bool( 'require_license', true ) ) { $this->_storage->require_license_activation = false; } if ( $this->is_theme() ) { $this->_register_account_hooks(); } if ( $this->is_user_in_admin() && $this->is_clone() ) { if ( empty( FS_Clone_Manager::instance()->get_clone_identification_timestamp() ) ) { FS_Clone_Manager::instance()->store_clone_identification_timestamp(); } FS_Clone_Manager::instance()->maybe_update_clone_resolution_support_flag( $this->_storage->sdk_last_version ); $this->send_pending_clone_update_once(); } } /** * Special user recovery mechanism. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number|null $site_user_id * * @return \FS_User|mixed */ private function sync_user_by_current_install( $site_user_id = null ) { $site_user_id = FS_Site::is_valid_id( $site_user_id ) ? $site_user_id : $this->_site->user_id; $api = $this->get_api_site_scope(); $uid = $this->get_anonymous_id(); $request_path = "/users/{$site_user_id}.json?uid={$uid}"; $result = $api->get( $request_path, false, WP_FS__TIME_10_MIN_IN_SEC ); if ( $this->is_api_result_entity( $result ) ) { $user = new FS_User( $result ); $this->_user = $user; $this->_store_user(); return $user; } $error_code = FS_Api::get_error_code( $result ); if ( in_array( $error_code, array( 'invalid_unique_id', 'user_cannot_be_recovered' ) ) ) { /** * Those API errors will continue coming and are not recoverable with the * current site's data. Therefore, extend the API call's cached result to 7 days. */ $api->update_cache_expiration( $request_path, WP_FS__TIME_WEEK_IN_SEC ); } return $result; } /** * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param FS_User $user * @param FS_Site $site * @param bool|array $plans */ private function _set_account( FS_User $user, FS_Site $site, $plans = false ) { $site->user_id = $user->id; $this->_site = $site; $this->_user = $user; if ( false !== $plans ) { $this->_plans = $plans; } $this->send_install_update(); $this->_store_account(); } /** * Get a sanitized array with the WordPress version, SDK version, and PHP version. * Each version is trimmed after the 16th char. * * @author Vova Feldman (@svovaf) * @since 2.2.1 * * @return array */ private function get_versions() { $versions = array(); $versions['sdk_version'] = $this->version; // Collect these diagnostic information only if it's allowed. if ( FS_Permission_Manager::instance( $this )->is_diagnostic_tracking_allowed() ) { $versions['platform_version'] = get_bloginfo( 'version' ); $versions['programming_language_version'] = phpversion(); } foreach ( $versions as $k => $version ) { $versions[ $k ] = self::get_api_sanitized_property( $k, $version ); } return $versions; } /** * Get sanitized site language. * * @param string $language * @param int $max_len * * @since 2.5.1 * @author Vova Feldman (@svovaf) * * @return string */ private static function get_sanitized_language( $language = '', $max_len = self::LANGUAGE_MAX_CHARS ) { if ( empty( $language ) ) { $language = get_bloginfo( 'language' ); } return substr( $language, 0, $max_len ); } /** * Get core version stripped from pre-release and build. * * @since 2.5.1 * @author Vova Feldman (@svovaf) * * @param string $version * @param int $parts * @param int $max_len * @param bool $include_pre_release * * @return string */ private static function get_core_version( $version, $parts = 3, $max_len = self::VERSION_MAX_CHARS, $include_pre_release = false ) { if ( empty( $version ) ) { // Version is empty. return ''; } if ( is_numeric( $version ) ) { $is_float_version = is_float( $version ); $version = (string) $version; /** * Casting a whole float number to a string cuts the decimal point. This part make sure to add the missing decimal part to the version. */ if ( $is_float_version && false === strpos( $version, '.' ) ) { $version .= '.0'; } } if ( ! is_string( $version ) ) { return ''; } if ( $parts < 1 ) { return ''; } $pre_release_regex = $include_pre_release ? '(\-(alpha|beta|RC)([0-9]+)?)?' : ''; if ( 0 === preg_match( '/^([0-9]+(\.[0-9]+){0,' . ( $parts - 1 ) . '}' . $pre_release_regex . ')/i', $version, $matches ) ) { // Version is not starting with a digit. return ''; } return substr( $matches[1], 0, $max_len ); } /** * @param string $prop * @param mixed $val * * @return mixed *@author Vova Feldman (@svovaf) * * @since 2.5.1 */ private static function get_api_sanitized_property( $prop, $val ) { if ( ! is_string( $val ) || empty( $val ) ) { return $val; } switch ( $prop ) { case 'programming_language_version': // Get core PHP version, which can have up to 3 parts (ignore pre-releases). return self::get_core_version( $val ); case 'platform_version': // Get the exact WordPress version, which can have up to 3 parts (including pre-releases). return self::get_core_version( $val, 3, self::VERSION_MAX_CHARS, true ); case 'sdk_version': // Get the exact SDK version, which can have up to 4 parts. return self::get_core_version( $val, 4 ); case 'version': // Get the entire version but just limited in length. return substr( $val, 0, self::VERSION_MAX_CHARS ); case 'language': return self::get_sanitized_language( $val ); default: return $val; } } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @return bool */ function has_beta_update() { return ( ! empty( $this->_storage->beta_data ) && ( true === $this->_storage->beta_data['is_beta'] ) && version_compare( $this->_storage->beta_data['version'], $this->get_plugin_version(), '>' ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @return bool */ function is_beta() { return ( ! empty( $this->_storage->beta_data ) && ( true === $this->_storage->beta_data['is_beta'] ) && ( $this->get_plugin_version() === $this->_storage->beta_data['version'] ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.4 * * @param array $override_with * @param bool|int|null $network_level_or_blog_id If true, return params for network level opt-in. If integer, get params for specified blog in the network. * * @return array */ function get_opt_in_params( $override_with = array(), $network_level_or_blog_id = null ) { $this->_logger->entrance(); $current_user = self::_get_current_wp_user(); $activation_action = $this->get_unique_affix() . '_activate_new'; $return_url = $this->is_anonymous() ? // If skipped already, then return to the account page. $this->get_account_url( $activation_action, array(), false ) : // Return to the module's main page. $this->get_after_activation_url( 'after_connect_url', array( 'fs_action' => $activation_action ) ); $versions = $this->get_versions(); $params = array_merge( $versions, array( 'user_firstname' => $current_user->user_firstname, 'user_lastname' => $current_user->user_lastname, 'user_email' => $current_user->user_email, 'plugin_slug' => $this->_slug, 'plugin_id' => $this->get_id(), 'plugin_public_key' => $this->get_public_key(), 'plugin_version' => $this->get_plugin_version(), 'return_url' => fs_nonce_url( $return_url, $activation_action ), 'account_url' => fs_nonce_url( $this->_get_admin_page_url( 'account', array( 'fs_action' => 'sync_user' ) ), 'sync_user' ), 'is_premium' => $this->is_premium(), 'is_active' => true, 'is_uninstalled' => false, 'is_localhost' => WP_FS__IS_LOCALHOST, ) ); if ( $this->is_addon() ) { $parent_fs = $this->get_parent_instance(); $params['parent_plugin_slug'] = $parent_fs->_slug; $params['parent_plugin_id'] = $parent_fs->get_id(); } if ( true === $network_level_or_blog_id ) { if ( ! isset( $override_with['sites'] ) ) { $params['sites'] = $this->get_sites_for_network_level_optin(); } } else { $site = is_numeric( $network_level_or_blog_id ) ? array( 'blog_id' => $network_level_or_blog_id ) : null; $site = $this->get_site_info( $site ); $diagnostic_info = array(); if ( FS_Permission_Manager::instance( $this )->is_diagnostic_tracking_allowed() ) { $diagnostic_info = array( 'site_name' => $site['title'], 'language' => self::get_sanitized_language( $site['language'] ), ); } $params = array_merge( $params, $diagnostic_info, array( 'site_uid' => $site['uid'], 'site_url' => $site['url'], ) ); } if ( $this->is_pending_activation() && ! empty( $this->_storage->pending_license_key ) ) { $params['license_key'] = $this->_storage->pending_license_key; } if ( WP_FS__SKIP_EMAIL_ACTIVATION && $this->has_secret_key() ) { // Even though rand() is known for its security issues, // the timestamp adds another layer of protection. // It would be very hard for an attacker to get the secret key form here. // Plus, this should never run in production since the secret should never // be included in the production version. $params['ts'] = WP_FS__SCRIPT_START_TIME; $params['salt'] = md5( uniqid( rand() ) ); $params['secure'] = md5( $params['ts'] . $params['salt'] . $this->get_secret_key() ); } if ( is_multisite() && function_exists( 'get_network' ) ) { $params['network_uid'] = $this->get_anonymous_network_id(); } return array_merge( $params, $override_with ); } /** * 1. If successful opt-in or pending activation returns the next page that the user should be redirected to. * 2. If there was an API error, return the API result. * * @author Vova Feldman (@svovaf) * @since 1.1.7.4 * * @param string|bool $email * @param string|bool $first * @param string|bool $last * @param string|bool $license_key * @param bool $is_uninstall If "true", this means that the module is currently being uninstalled. * In this case, the user and site info will be sent to the server but no * data will be saved to the WP installation's database. * @param number|bool $trial_plan_id * @param bool $is_disconnected Whether to opt in without tracking. * @param null|bool $is_marketing_allowed * @param array $sites If network-level opt-in, an array of containing details of sites. * @param bool $redirect * * @return string|object * @use WP_Error */ function opt_in( $email = false, $first = false, $last = false, $license_key = false, $is_uninstall = false, $trial_plan_id = false, $is_disconnected = false, $is_marketing_allowed = null, $sites = array(), $redirect = true ) { $this->_logger->entrance(); if ( false === $email ) { $current_user = self::_get_current_wp_user(); $email = $current_user->user_email; } /** * @since 1.2.1 If activating with license key, ignore the context-user * since the user will be automatically loaded from the license. */ if ( empty( $license_key ) ) { // Clean up pending license if opt-ing in again. $this->_storage->remove( 'pending_license_key' ); if ( ! $is_uninstall ) { $fs_user = Freemius::_get_user_by_email( $email ); if ( is_object( $fs_user ) && ! $this->is_pending_activation() ) { return $this->install_with_user( $fs_user, false, $trial_plan_id, $redirect, true, $sites ); } } } $user_info = array(); if ( ! empty( $email ) ) { $user_info['user_email'] = $email; } if ( ! empty( $first ) ) { $user_info['user_firstname'] = $first; } if ( ! empty( $last ) ) { $user_info['user_lastname'] = $last; } if ( ! empty( $sites ) ) { $is_network = true; $user_info['sites'] = $sites; } else { $is_network = false; } $params = $this->get_opt_in_params( $user_info, $is_network ); $filtered_license_key = false; if ( is_string( $license_key ) ) { $filtered_license_key = $this->apply_filters( 'license_key', $license_key ); $params['license_key'] = $filtered_license_key; } else if ( FS_Plugin_Plan::is_valid_id( $trial_plan_id ) ) { $params['trial_plan_id'] = $trial_plan_id; } if ( $is_uninstall ) { $params['uninstall_params'] = array( 'reason_id' => $this->_storage->uninstall_reason->id, 'reason_info' => $this->_storage->uninstall_reason->info ); } if ( isset( $params['license_key'] ) ) { $fs_user = Freemius::_get_user_by_email( $email ); if ( is_object( $fs_user ) ) { /** * If opting in with a context license and the context WP Admin user already opted in * before from the current site, add the user context security params to avoid the * unnecessary email activation when the context license is owned by the same context user. * * @author Leo Fajardo (@leorw) * @since 1.2.3 */ $params = array_merge( $params, FS_Security::instance()->get_context_params( $fs_user, false, 'install_with_existing_user' ) ); } } if ( is_bool( $is_marketing_allowed ) ) { $params['is_marketing_allowed'] = $is_marketing_allowed; } $params['is_disconnected'] = $is_disconnected; $params['format'] = 'json'; $params['is_extensions_tracking_allowed'] = FS_Permission_Manager::instance( $this )->is_extensions_tracking_allowed(); $params['is_diagnostic_tracking_allowed'] = FS_Permission_Manager::instance( $this )->is_diagnostic_tracking_allowed(); $request = array( 'method' => 'POST', 'body' => $params, 'timeout' => 60, ); $url = $this->add_show_pending( WP_FS__ADDRESS . '/action/service/user/install/' ); $response = self::safe_remote_post( $url, $request ); if ( is_wp_error( $response ) ) { /** * @var WP_Error $response */ $result = new stdClass(); $error_code = $response->get_error_code(); $error_type = str_replace( ' ', '', ucwords( str_replace( '_', ' ', $error_code ) ) ); $result->error = (object) array( 'type' => $error_type, 'message' => $response->get_error_message(), 'code' => $error_code, 'http' => 402 ); $this->maybe_modify_api_curl_error_message( $result ); if ( FS_Api::is_blocked( $result ) ) { $result->error->message = $this->generate_api_blocked_notice_message_from_result( $result ); } $is_connected = null; if ( empty( $license_key ) && $this->is_enable_anonymous() ) { $this->skip_connection( fs_is_network_admin() ); $is_connected = ( ! FS_Api::is_blocked( $result ) ); } $this->update_connectivity_info( $is_connected ); return $result; } $this->update_connectivity_info( true ); // Module is being uninstalled, don't handle the returned data. if ( $is_uninstall ) { return true; } /** * When json_decode() executed on PHP 5.2 with an invalid JSON, it will throw a PHP warning. Unfortunately, the new Theme Check doesn't allow PHP silencing and the theme review team isn't open to change that, therefore, instead of using `@json_decode()` we had to use the method without the `@` directive. * * @author Vova Feldman (@svovaf) * @since 1.2.3 * @link https://themes.trac.wordpress.org/ticket/46134#comment:5 * @link https://themes.trac.wordpress.org/ticket/46134#comment:9 * @link https://themes.trac.wordpress.org/ticket/46134#comment:12 * @link https://themes.trac.wordpress.org/ticket/46134#comment:14 */ $decoded = is_string( $response['body'] ) ? json_decode( $response['body'] ) : null; if ( empty( $decoded ) ) { return false; } if ( ! $this->is_api_result_object( $decoded ) ) { if ( ! empty( $params['license_key'] ) ) { // Pass the fully entered license key to the failure handler. $params['license_key'] = $license_key; } return $is_uninstall ? $decoded : $this->apply_filters( 'after_install_failure', $decoded, $params ); } else if ( isset( $decoded->pending_activation ) && $decoded->pending_activation ) { if ( $is_network ) { $site_ids = array(); foreach ( $sites as $site ) { $site_ids[] = $site['blog_id']; } /** * Store the sites so that they can be installed once the user has clicked on the activation link * in the email. * * @author Leo Fajardo (@leorw) */ $this->_storage->pending_sites_info = array( 'blog_ids' => $site_ids, 'license_key' => $license_key, 'trial_plan_id' => $trial_plan_id ); } // Pending activation, add message. return $this->set_pending_confirmation( ( isset( $decoded->email ) ? $decoded->email : true ), false, $filtered_license_key, ! empty( $params['trial_plan_id'] ), isset( $decoded->is_suspicious_email ) && $decoded->is_suspicious_email ); } else if ( isset( $decoded->install_secret_key ) ) { return $this->install_with_new_user( $decoded->user_id, $decoded->user_public_key, $decoded->user_secret_key, ( isset( $decoded->is_marketing_allowed ) && ! is_null( $decoded->is_marketing_allowed ) ? $decoded->is_marketing_allowed : null ), ( isset( $decoded->is_extensions_tracking_allowed ) && ! is_null( $decoded->is_extensions_tracking_allowed ) ? $decoded->is_extensions_tracking_allowed : null ), ( isset( $decoded->is_diagnostic_tracking_allowed ) && ! is_null( $decoded->is_diagnostic_tracking_allowed ) ? $decoded->is_diagnostic_tracking_allowed : null ), $decoded->install_id, $decoded->install_public_key, $decoded->install_secret_key, false ); } else if ( is_array( $decoded->installs ) ) { return $this->install_many_with_new_user( $decoded->user_id, $decoded->user_public_key, $decoded->user_secret_key, ( isset( $decoded->is_marketing_allowed ) && ! is_null( $decoded->is_marketing_allowed ) ? $decoded->is_marketing_allowed : null ), ( isset( $decoded->is_extensions_tracking_allowed ) && ! is_null( $decoded->is_extensions_tracking_allowed ) ? $decoded->is_extensions_tracking_allowed : null ), ( isset( $decoded->is_diagnostic_tracking_allowed ) && ! is_null( $decoded->is_diagnostic_tracking_allowed ) ? $decoded->is_diagnostic_tracking_allowed : null ), $decoded->installs, false ); } return $decoded; } /** * Set user and site identities. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param FS_User $user * @param FS_Site $site * @param bool $redirect * @param bool $auto_install Since 1.2.1.7 If `true` and setting up an account with a valid license, will * redirect (or return a URL) to the account page with a special parameter to * trigger the auto installation processes. * * @return string If redirect is `false`, returns the next page the user should be redirected to. */ function setup_account( FS_User $user, FS_Site $site, $redirect = true, $auto_install = false ) { return $this->setup_network_account( $user, array( $site ), $redirect, $auto_install, false ); } /** * Set user and site identities. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param FS_User $user * @param FS_Site[] $installs * @param bool $redirect * @param bool $auto_install Since 1.2.1.7 If `true` and setting up an account with a valid license, will redirect (or return a URL) to the account page with a special parameter to trigger the auto installation processes. * @param bool $is_network_level_opt_in * * @return string If redirect is `false`, returns the next page the user should be redirected to. */ function setup_network_account( FS_User $user, array $installs, $redirect = true, $auto_install = false, $is_network_level_opt_in = true ) { $first_install = $installs[0]; $this->_user = $user; $this->_site = $first_install; $this->_sync_plans(); if ( $this->_storage->handle_gdpr_admin_notice && $this->should_handle_gdpr_admin_notice() && FS_GDPR_Manager::instance()->should_show_opt_in_notice() ) { /** * Clear user lock after an opt-in. */ require_once WP_FS__DIR_INCLUDES . '/class-fs-user-lock.php'; FS_User_Lock::instance()->unlock(); } if ( 1 < count( $installs ) ) { // Only network level opt-in can have more than one install. $is_network_level_opt_in = true; } $this->update_connectivity_info( true ); // $is_network_level_opt_in = self::is_ajax_action_static( 'network_activate', $this->_module_id ); // If Freemius was OFF before, turn it on. $this->turn_on(); $this->handle_account_connection( $installs, ( ! $this->_is_network_active || ! $is_network_level_opt_in ) ); if ( is_numeric( $first_install->license_id ) ) { $this->set_license( $this->_get_license_by_id( $first_install->license_id ) ); } $this->_admin_notices->remove_sticky( 'connect_account' ); if ( $this->is_pending_activation() || ! $this->has_settings_menu() ) { $this->clear_pending_activation_mode(); if ( ! $this->is_paying_or_trial() ) { $this->_admin_notices->add_sticky( sprintf( $this->get_text_inline( '%s opt-in was successfully completed.', 'plugin-x-activation-message' ), '' . $this->get_plugin_name() . '' ), 'activation_complete' ); } } if ( $this->is_paying_or_trial() ) { if ( ! $this->is_premium() || ! $this->has_premium_version() || ! $this->has_settings_menu() ) { if ( $this->is_paying() ) { $this->add_complete_upgrade_instructions_notice( sprintf( $this->get_text_inline( 'Your account was successfully activated with the %s plan.', 'activation-with-plan-x-message' ), $this->get_plan_title() ), 'plan_upgraded' ); } else { $trial_plan = $this->get_trial_plan(); $this->add_complete_upgrade_instructions_notice( sprintf( $this->get_text_inline( 'Your trial has been successfully started.', 'trial-started-message' ), '' . $this->get_plugin_name() . '' ), 'trial_started', $trial_plan->title ); } } $this->_admin_notices->remove_sticky( array( 'trial_promotion', ) ); } $plugin_id = fs_request_get( 'plugin_id', false ); // Store activation time ONLY for plugins & themes (not add-ons). if ( ! is_numeric( $plugin_id ) || ( $plugin_id == $this->_plugin->id ) ) { if ( empty( $this->_storage->activation_timestamp ) ) { $this->_storage->activation_timestamp = WP_FS__SCRIPT_START_TIME; } } $next_page = ''; $extra = array(); if ( $auto_install ) { $extra['auto_install'] = 'true'; } if ( is_numeric( $plugin_id ) ) { /** * @author Leo Fajardo (@leorw) * @since 1.2.1.6 * * Also sync the license after an anonymous user subscribes. */ if ( $this->is_anonymous() || $plugin_id != $this->_plugin->id ) { // Add-on was installed - sync license right after install. $next_page = $this->_get_sync_license_url( $plugin_id, true, $extra ); } } else { /** * @author Vova Feldman (@svovaf) * @since 1.1.9 If site installed with a valid license, sync license. */ if ( $this->is_paying() ) { $this->_sync_plugin_license( true, // Installs data is already synced in the beginning of this method directly or via _set_account(). false ); } // Reload the page with the keys. $next_page = $this->is_anonymous() ? // If user previously skipped, redirect to account page. $this->get_account_url( false, $extra ) : $this->get_after_activation_url( 'after_connect_url', array(), $is_network_level_opt_in ); } if ( ! empty( $next_page ) && $redirect ) { fs_redirect( $next_page ); } return $next_page; } /** * Install plugin with new user information after approval. * * @author Vova Feldman (@svovaf) * @since 1.0.7 */ function _install_with_new_user() { $this->_logger->entrance(); if ( $this->is_registered() ) { return; } $has_pending_activation_confirmation_param = fs_request_has( 'pending_activation' ); $this->update_license_required_permissions_if_anonymous(); if ( ( $this->is_plugin() && fs_request_is_action( $this->get_unique_affix() . '_activate_new' ) ) || // @todo This logic should be improved because it's executed on every load of a theme. $this->is_theme() ) { // check_admin_referer( $this->_slug . '_activate_new' ); if ( fs_request_has( 'user_secret_key' ) ) { if ( fs_is_network_admin() && isset( $this->_storage->pending_sites_info ) ) { $pending_sites_info = $this->_storage->pending_sites_info; $this->install_many_pending_with_user( fs_request_get( 'user_id' ), fs_request_get_raw( 'user_public_key' ), fs_request_get_raw( 'user_secret_key' ), fs_request_get_bool( 'is_marketing_allowed', null ), fs_request_get_bool( 'is_extensions_tracking_allowed', null ), fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ), $pending_sites_info['blog_ids'], $pending_sites_info['license_key'], $pending_sites_info['trial_plan_id'] ); } else { $this->install_with_new_user( fs_request_get( 'user_id' ), fs_request_get_raw( 'user_public_key' ), fs_request_get_raw( 'user_secret_key' ), fs_request_get_bool( 'is_marketing_allowed', null ), fs_request_get_bool( 'is_extensions_tracking_allowed', null ), fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ), fs_request_get( 'install_id' ), fs_request_get_raw( 'install_public_key' ), fs_request_get_raw( 'install_secret_key' ), true, fs_request_get_bool( 'auto_install' ) ); } } else if ( $has_pending_activation_confirmation_param ) { $this->set_pending_confirmation( fs_request_get( 'user_email' ), true, false, false, fs_request_get_bool( 'is_suspicious_email' ), fs_request_get_bool( 'has_upgrade_context' ), fs_request_get( 'support_email_address' ) ); } } } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $id * @param string $public_key * @param string $secret_key * * @return \FS_User */ private function setup_user( $id, $public_key, $secret_key ) { $user = self::_get_user_by_id( $id ); if ( is_object( $user ) ) { $this->_user = $user; } else { $user = new FS_User(); $user->id = $id; $user->public_key = $public_key; $user->secret_key = $secret_key; $this->_user = $user; $user_result = $this->get_api_user_scope()->get(); $user = new FS_User( $user_result ); $this->_user = $user; $this->_store_user(); } return $user; } /** * Install plugin with new user. * * @author Vova Feldman (@svovaf) * @since 1.1.7.4 * * @param number $user_id * @param string $user_public_key * @param string $user_secret_key * @param bool|null $is_marketing_allowed * @param bool|null $is_extensions_tracking_allowed Since 2.3.2 * @param bool|null $is_diagnostic_tracking_allowed Since 2.5.0.2 * @param number $install_id * @param string $install_public_key * @param string $install_secret_key * @param bool $redirect * @param bool $auto_install Since 1.2.1.7 If `true` and setting up an account with a valid license, will redirect (or return a URL) to the account page with a special parameter to trigger the auto installation processes. * * @return string If redirect is `false`, returns the next page the user should be redirected to. */ private function install_with_new_user( $user_id, $user_public_key, $user_secret_key, $is_marketing_allowed, $is_extensions_tracking_allowed, $is_diagnostic_tracking_allowed, $install_id, $install_public_key, $install_secret_key, $redirect = true, $auto_install = false ) { /** * This method is also executed after opting in with a license key since the * license can be potentially associated with a different owner. * * @since 2.0.0 */ $user = self::_get_user_by_id( $user_id ); if ( ! is_object( $user ) ) { $user = new FS_User(); $user->id = $user_id; $user->public_key = $user_public_key; $user->secret_key = $user_secret_key; $this->_user = $user; $user_result = $this->get_api_user_scope()->get(); $user = new FS_User( $user_result ); } $this->_user = $user; $site = new FS_Site(); $site->id = $install_id; $site->public_key = $install_public_key; $site->secret_key = $install_secret_key; $this->_site = $site; $site_result = $this->get_api_site_scope( true )->get(); $site = new FS_Site( $site_result ); $this->_site = $site; if ( ! is_null( $is_marketing_allowed ) ) { $this->disable_opt_in_notice_and_lock_user(); } FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array( FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed, FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed, ) ); return $this->setup_account( $this->_user, $this->_site, $redirect, $auto_install ); } /** * Install plugin with user. * * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param number $user_id * @param string $user_public_key * @param string $user_secret_key * @param bool|null $is_marketing_allowed * @param bool|null $is_extensions_tracking_allowed Since 2.3.2 * @param bool|null $is_diagnostic_tracking_allowed Since 2.5.0.2 * @param array $site_ids * @param bool $license_key * @param bool $trial_plan_id * @param bool $redirect * * @return void */ private function install_many_pending_with_user( $user_id, $user_public_key, $user_secret_key, $is_marketing_allowed, $is_extensions_tracking_allowed, $is_diagnostic_tracking_allowed, $site_ids, $license_key = false, $trial_plan_id = false, $redirect = true ) { $user = $this->setup_user( $user_id, $user_public_key, $user_secret_key ); if ( ! is_null( $is_marketing_allowed ) ) { $this->disable_opt_in_notice_and_lock_user(); } FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array( FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed, FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed, ) ); $sites = array(); foreach ( $site_ids as $site_id ) { $sites[] = $this->get_site_info( array( 'blog_id' => $site_id ) ); } $this->install_with_user( $user, $license_key, $trial_plan_id, $redirect, true, $sites ); } /** * Multi-site install with a new user. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $user_id * @param string $user_public_key * @param string $user_secret_key * @param bool|null $is_marketing_allowed * @param bool|null $is_extensions_tracking_allowed Since 2.3.2 * @param bool|null $is_diagnostic_tracking_allowed Since 2.5.0.2 * @param object[] $installs * @param bool $redirect * @param bool $auto_install Since 1.2.1.7 If `true` and setting up an account with a valid license, will redirect (or return a URL) to the account page with a special parameter to trigger the auto installation processes. * * @return string If redirect is `false`, returns the next page the user should be redirected to. */ private function install_many_with_new_user( $user_id, $user_public_key, $user_secret_key, $is_marketing_allowed, $is_extensions_tracking_allowed, $is_diagnostic_tracking_allowed, array $installs, $redirect = true, $auto_install = false ) { $this->setup_user( $user_id, $user_public_key, $user_secret_key ); if ( ! is_null( $is_marketing_allowed ) ) { $this->disable_opt_in_notice_and_lock_user(); } FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array( FS_Permission_Manager::PERMISSION_DIAGNOSTIC => $is_diagnostic_tracking_allowed, FS_Permission_Manager::PERMISSION_EXTENSIONS => $is_extensions_tracking_allowed, ) ); $install_ids = array(); foreach ( $installs as $install ) { $install_ids[] = $install->id; } $items_per_request = 25; $left = count( $install_ids ); $offset = 0; $installs = array(); while ( $left > 0 ) { $result = $this->get_api_user_scope()->get( "/plugins/{$this->_module_id}/installs.json?ids=" . implode( ',', array_slice( $install_ids, $offset, $items_per_request ) ) ); if ( ! $this->is_api_result_object( $result, 'installs' ) ) { // @todo Handle API error. } $installs = array_merge( $installs, $result->installs ); $left -= $items_per_request; $offset += $items_per_request; } foreach ( $installs as &$install ) { $install = new FS_Site( $install ); } return $this->setup_network_account( $this->_user, $installs, $redirect, $auto_install ); } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.4 * * @param string|bool $email * @param bool $redirect * @param string|bool $license_key Since 1.2.1.5 * @param bool $is_pending_trial Since 1.2.1.5 * @param bool $is_suspicious_email Since 2.5.0 * @param bool $has_upgrade_context Since 2.5.3 * @param bool|string $support_email_address Since 2.5.3 * * @return string Since 1.2.1.5 if $redirect is `false`, return the pending activation page. */ private function set_pending_confirmation( $email = false, $redirect = true, $license_key = false, $is_pending_trial = false, $is_suspicious_email = false, $has_upgrade_context = false, $support_email_address = false ) { $is_network_admin = fs_is_network_admin(); if ( $this->_ignore_pending_mode && ! $has_upgrade_context ) { /** * If explicitly asked to ignore pending mode, set to anonymous mode * if require confirmation before finalizing the opt-in except after completing a purchase (otherwise, in this case, they wouldn't see any notice telling them that they should receive their license key via email). * * @author Vova Feldman * @since 1.2.1.6 */ $this->skip_connection( $is_network_admin ); } else { // Install must be activated via email since // user with the same email already exist. $this->_storage->is_pending_activation = true; $this->_add_pending_activation_notice( $email, $is_pending_trial, $is_suspicious_email, $has_upgrade_context, $support_email_address ); } if ( ! empty( $license_key ) ) { $this->_storage->pending_license_key = $license_key; } // Remove the opt-in sticky notice. $this->_admin_notices->remove_sticky( array( 'connect_account', 'trial_promotion', ) ); $next_page = $this->get_after_activation_url( 'after_pending_connect_url' ); if ( $redirect ) { // Reload the page with a pending activation message. fs_redirect( $next_page ); } return $next_page; } /** * Install plugin with current logged WP user info. * * @author Vova Feldman (@svovaf) * @since 1.0.7 */ function _install_with_current_user() { $this->_logger->entrance(); if ( $this->is_registered() ) { return; } if ( fs_request_is_action( $this->get_unique_affix() . '_activate_existing' ) && fs_request_is_post() ) { check_admin_referer( $this->get_unique_affix() . '_activate_existing' ); /** * @author Vova Feldman (@svovaf) * @since 1.1.9 Add license key if given. */ $license_key = fs_request_get_raw( 'license_secret_key' ); FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( array( FS_Permission_Manager::PERMISSION_DIAGNOSTIC => fs_request_get_bool( 'is_diagnostic_tracking_allowed', null ), FS_Permission_Manager::PERMISSION_EXTENSIONS => fs_request_get_bool( 'is_extensions_tracking_allowed', null ), ) ); $this->install_with_current_user( $license_key ); } } /** * @author Vova Feldman (@svovaf) * @since 1.1.7.4 * * @param string|bool $license_key * @param number|bool $trial_plan_id * @param array $sites Since 2.0.0 * @param bool $redirect * * @return object|string If redirect is `false`, returns the next page the user should be redirected to, or the API error object if failed to install. */ function install_with_current_user( $license_key = false, $trial_plan_id = false, $sites = array(), $redirect = true ) { // Get current logged WP user. $current_user = self::_get_current_wp_user(); // Find the relevant FS user by the email. $user = self::_get_user_by_email( $current_user->user_email ); return $this->install_with_user( $user, $license_key, $trial_plan_id, $redirect, true, $sites ); } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_User $user * @param string|bool $license_key * @param number|bool $trial_plan_id * @param bool $redirect * @param bool $setup_account Since 2.0.0. When set to FALSE, executes a light installation without setting up the account as if it's the first opt-in. * @param array $sites Since 2.0.0. If not empty, should be a collection of site details for the bulk install API request. * * @return \FS_Site|object|string If redirect is `false`, returns the next page the user should be redirected to, or the API error object if failed to install. If $setup_account is set to `false`, return the newly created install. */ function install_with_user( FS_User $user, $license_key = false, $trial_plan_id = false, $redirect = true, $setup_account = true, $sites = array() ) { // We have to set the user before getting user scope API handler. $this->_user = $user; // Install the plugin. $result = $this->create_installs_with_user( $user, $license_key, $trial_plan_id, $sites, $redirect ); if ( ! $this->is_api_result_entity( $result ) && ! $this->is_api_result_object( $result, 'installs' ) ) { // @todo Handler potential API error of the $result } if ( empty( $sites ) ) { $site = new FS_Site( $result ); $this->_site = $site; if ( ! $setup_account ) { $this->_store_site(); $this->sync_plan_if_not_exist( $site->plan_id ); if ( ! empty( $license_key ) && FS_Plugin_License::is_valid_id( $site->license_id ) ) { $this->sync_license_if_not_exist( $site->license_id, $license_key ); } $this->_admin_notices->remove_sticky( 'connect_account', false ); return $site; } return $this->setup_account( $this->_user, $this->_site, $redirect ); } else { $installs = array(); foreach ( $result->installs as $install ) { $installs[] = new FS_Site( $install ); } return $this->setup_network_account( $user, $installs, $redirect ); } } /** * Initiate an API request to create a collection of installs. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_User $user * @param bool $license_key * @param bool $trial_plan_id * @param array $sites * @param bool $redirect * @param bool $silent * * @return object|mixed */ private function create_installs_with_user( FS_User $user, $license_key = false, $trial_plan_id = false, $sites = array(), $redirect = false, $silent = false ) { $extra_install_params = array( 'uid' => $this->get_anonymous_id(), 'is_disconnected' => false, ); if ( ! empty( $license_key ) ) { $extra_install_params['license_key'] = $this->apply_filters( 'license_key', $license_key ); if ( $silent ) { $extra_install_params['ignore_license_owner'] = true; } } else if ( FS_Plugin_Plan::is_valid_id( $trial_plan_id ) ) { $extra_install_params['trial_plan_id'] = $trial_plan_id; } if ( ! empty( $sites ) ) { $extra_install_params['sites'] = $sites; } $args = $this->get_install_data_for_api( $extra_install_params, false, false ); // Install the plugin. $result = $this->get_api_user_scope_by_user( $user )->call( "/plugins/{$this->get_id()}/installs.json", 'post', $args ); if ( ! $this->is_api_result_entity( $result ) && ! $this->is_api_result_object( $result, 'installs' ) ) { if ( ! empty( $args['license_key'] ) ) { // Pass the fully entered license key to the failure handler. $args['license_key'] = $license_key; } $result = $this->apply_filters( 'after_install_failure', $result, $args ); if ( ! $silent ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' . $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '' . $result->error->message . '', $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 'error' ); } if ( $redirect ) { /** * We set the user before getting the user scope API handler, so the user became temporarily * registered (`is_registered() = true`). Since the API returned an error and we will redirect, * we have to set the user to `null`, otherwise, the user will be redirected to the wrong * activation page based on the return value of `is_registered()`. In addition, in case the * context plugin doesn't have a settings menu and the default page is the `Plugins` page, * misleading plugin activation errors will be shown on the `Plugins` page. * * @author Leo Fajardo (@leorw) */ $user = $this->_user; $this->_user = null; fs_redirect( $this->get_activation_url( array( 'error' => $result->error->message ) ) ); /** * Restore the user after the redirect, this is relevant when there are cases where the redirect will choose not to do anything. */ $this->_user = $user; } } return $result; } /** * Tries to activate add-on account based on parent plugin info. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param Freemius $parent_fs * @param bool|int|null $network_level_or_blog_id True for network level opt-in and integer for opt-in for specified blog in the network. * @param FS_Plugin_License $bundle_license Since 2.4.0. If provided, this license will be activated for the add-on. */ private function _activate_addon_account( Freemius $parent_fs, $network_level_or_blog_id = null, FS_Plugin_License $bundle_license = null ) { if ( $this->is_registered() ) { // Already activated. return; } $permission_ids = FS_Permission_Manager::get_all_permission_ids(); $permissions = array(); foreach ( $permission_ids as $permission_id ) { $permissions[ $permission_id ] = FS_Permission_Manager::instance( $parent_fs )->is_permission( $permission_id, true ); } FS_Permission_Manager::instance( $this )->update_permissions_tracking_flag( $permissions ); /** * Do not override the `uid` if network-level opt-in since the call to `get_sites_for_network_level_optin()` * already returns the data for the current blog. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $uid_param_to_override = ( true === $network_level_or_blog_id ) ? array() : array( 'uid' => $this->get_anonymous_id() ); $params = $this->get_install_data_for_api( $uid_param_to_override, false, false, /** * Do not include the data for the current blog if network-level opt-in since the call to `get_sites_for_network_level_optin` * already includes the data for it. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ ( true !== $network_level_or_blog_id ) ); if ( true === $network_level_or_blog_id ) { $params['sites'] = $this->get_sites_for_network_level_optin(); if ( empty( $params['sites'] ) ) { return; } } if ( is_object( $bundle_license ) ) { $params['license_key'] = $bundle_license->secret_key; } // Activate add-on with parent plugin credentials. $result = $parent_fs->get_api_site_scope()->call( "/addons/{$this->_plugin->id}/installs.json", 'post', $params ); if ( ! $this->is_api_result_object( $result, 'installs' ) ) { if ( is_object( $bundle_license ) ) { /** * When a license object is provided, it's an attempt by the SDK to activate a bundle license and not a user-initiated action, therefore, do not show any admin notice to avoid confusion (e.g.: the notice will show up just above the opt-in link). If the license activation fails, the admin will see an opt-in link instead. * * @author Leo Fajardo (@leorw) * @since 2.4.0 */ } else { $error_message = FS_Api::is_api_error_object( $result ) ? $result->error->message : $this->get_text_inline( 'An unknown error has occurred.', 'unknown-error' ); $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' . $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '' . $error_message . '', $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 'error' ); } return; } $addon_installs = $result->installs; foreach ( $addon_installs as $key => $addon_install ) { $addon_installs[ $key ] = new FS_Site( $addon_install ); } $first_install = $addon_installs[0]; // Get user information based on parent's plugin. $user = $parent_fs->get_user(); // First of all, set site and user info - otherwise we won't // be able to invoke API calls. $this->_site = $first_install; $this->_user = $user; // Sync add-on plans. $this->_sync_plans(); $this->handle_account_connection( $addon_installs, ! fs_is_network_admin() ); // Get site's current plan. //$this->_site->plan = $this->_get_plan_by_id( $this->_site->plan->id ); // Sync licenses. $this->_sync_licenses(); if ( ! fs_is_network_admin() ) { // Try to activate premium license. $this->_activate_license( true, $bundle_license ); if ( is_object( $bundle_license ) ) { $this->maybe_activate_bundle_license( $bundle_license ); } } else { if ( is_object( $bundle_license ) ) { $premium_license = $bundle_license; } else { $license_id = fs_request_get( 'license_id' ); if ( is_object( $this->_site ) && FS_Plugin_License::is_valid_id( $license_id ) && $license_id == $this->_site->license_id ) { // License is already activated. return; } $premium_license = FS_Plugin_License::is_valid_id( $license_id ) ? $this->_get_license_by_id( $license_id ) : $this->_get_available_premium_license(); } if ( is_object( $premium_license ) ) { $this->maybe_network_activate_addon_license( $premium_license ); } } } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param FS_Site[] $installs * @param bool $is_site_level */ private function handle_account_connection( $installs, $is_site_level ) { $first_install = $installs[0]; if ( $is_site_level ) { $this->_set_account( $this->_user, $first_install ); $this->do_action( 'after_account_connection', $this->_user, $first_install ); } else { $this->_store_user(); // Map site addresses to their blog IDs. $address_to_blog_map = $this->get_address_to_blog_map(); $first_blog_id = null; $blog_2_install_map = array(); foreach ( $installs as $install ) { $address = trailingslashit( fs_strip_url_protocol( $install->url ) ); $blog_id = $address_to_blog_map[ $address ]; $this->_store_site( true, $blog_id, $install ); if ( is_null( $first_blog_id ) ) { $first_blog_id = $blog_id; } $blog_2_install_map[ $blog_id ] = $install; } if ( ! FS_User::is_valid_id( $this->_storage->network_user_id ) || ! is_object( self::_get_user_by_id( $this->_storage->network_user_id ) ) ) { // Store network user. $this->_storage->network_user_id = $this->_user->id; } if ( ! FS_Site::is_valid_id( $this->_storage->network_install_blog_id ) ) { $this->_storage->network_install_blog_id = $first_blog_id; } if ( count( $installs ) === count( $address_to_blog_map ) ) { // Super admin opted in for all sites in the network. $this->_storage->is_network_connected = true; } $this->_store_licenses( false ); self::$_accounts->store(); // Don't sync the installs data on network upgrade if ( ! $this->network_upgrade_mode_completed() ) { $this->send_installs_update(); } $current_blog = get_current_blog_id(); foreach ( $blog_2_install_map as $blog_id => $install ) { $this->switch_to_blog( $blog_id ); $this->do_action( 'after_account_connection', $this->_user, $install ); } // Switch install context back to the first install. $this->switch_to_blog( $current_blog, $first_install, ( $this->_site->id != $first_install->id ) ); $this->do_action( 'after_network_account_connection', $this->_user, $blog_2_install_map ); } } /** * Tries to activate parent account based on add-on's info. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @param Freemius $parent_fs */ private function activate_parent_account( Freemius $parent_fs ) { if ( ! $this->is_addon() ) { // This is not an add-on. return; } if ( $parent_fs->is_registered() ) { // Already activated. return; } // Activate parent with add-on's user credentials. $parent_install = $this->get_api_user_scope()->call( "/plugins/{$parent_fs->_plugin->id}/installs.json", 'post', $parent_fs->get_install_data_for_api( array( 'uid' => $parent_fs->get_anonymous_id(), ), false, false ) ); if ( isset( $parent_install->error ) ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' . $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '' . $parent_install->error->message . '', $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 'error' ); return; } $parent_fs->_admin_notices->remove_sticky( 'connect_account' ); if ( $parent_fs->is_pending_activation() ) { $parent_fs->clear_pending_activation_mode(); } // Get user information based on parent's plugin. $user = $this->get_user(); // First of all, set site info - otherwise we won't // be able to invoke API calls. $parent_fs->_site = new FS_Site( $parent_install ); $parent_fs->_user = $user; // Sync add-on plans. $parent_fs->_sync_plans(); $parent_fs->update_license_required_permissions_if_anonymous(); $parent_fs->_set_account( $user, $parent_fs->_site ); } #endregion #---------------------------------------------------------------------------------- #region Admin Menu Items #---------------------------------------------------------------------------------- private $_menu_items = array(); /** * @author Vova Feldman (@svovaf) * @since 1.2.1.8 * * @return array */ function get_menu_items() { return $this->_menu_items; } /** * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return string */ function get_menu_slug() { return $this->_menu->get_slug(); } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 */ function _prepare_admin_menu() { // if ( ! $this->is_on() ) { // return; // } if ( is_object( $this->_site ) && ! $this->is_registered() ) { return; } /** * When running from a site admin with a network activated module and the connection * was NOT delegated and the user still haven't skipped or opted-in, then hide the * site level settings. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ $should_hide_site_admin_settings = ( $this->_is_network_active && ! fs_is_network_admin() && ! $this->is_delegated_connection() && ! $this->is_anonymous() && ! $this->is_registered() ); $should_hide_site_admin_settings = $this->apply_filters( 'should_hide_site_admin_settings_on_network_activation_mode', $should_hide_site_admin_settings ); if ( ( false === $this->has_api_connectivity() && ! $this->is_enable_anonymous() ) || $should_hide_site_admin_settings ) { $this->_menu->remove_menu_item( $should_hide_site_admin_settings ); } else { $this->do_action( fs_is_network_admin() ? 'before_network_admin_menu_init' : 'before_admin_menu_init' ); $this->add_menu_action(); $this->add_network_menu_when_missing(); $this->add_submenu_items(); } } /** * Admin dashboard menu items modifications. * * NOTE: admin_menu action executed before admin_init. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * */ private function add_menu_action() { if ( $this->is_activation_mode() ) { if ( $this->show_opt_in_on_setting_page() ) { $this->override_plugin_menu_with_activation(); } else { /** * Handle theme opt-in when the opt-in form shows as a dialog box in the themes page. */ if ( fs_request_is_action( $this->get_unique_affix() . '_activate_existing' ) ) { add_action( 'load-themes.php', array( &$this, '_install_with_current_user' ) ); } else if ( fs_request_is_action( $this->get_unique_affix() . '_activate_new' ) || fs_request_get_bool( 'pending_activation' ) ) { add_action( 'load-themes.php', array( &$this, '_install_with_new_user' ) ); } } } else { if ( ! $this->is_registered() ) { // If not registered try to install user. if ( fs_request_is_action( $this->get_unique_affix() . '_activate_new' ) ) { $this->_install_with_new_user(); } } else if ( fs_request_is_action( 'sync_user' ) && ( ! $this->has_settings_menu() || $this->show_opt_in_on_themes_page() ) ) { $this->_handle_account_user_sync(); } } } /** * @author Vova Feldman (@svovaf) * @since 1.0.1 */ function _redirect_on_clicked_menu_link() { $this->_logger->entrance(); $page = fs_request_get('page'); $page = is_string($page) ? strtolower($page) : ''; $this->_logger->log( 'page = ' . $page ); foreach ( $this->_menu_items as $priority => $items ) { foreach ( $items as $item ) { if ( isset( $item['url'] ) ) { if ( $page === $this->_menu->get_slug( strtolower( $item['menu_slug'] ) ) ) { $this->_logger->log( 'Redirecting to ' . $item['url'] ); fs_redirect( $item['url'] ); } } } } } /** * Remove plugin's all admin menu items & pages, and replace with activation page. * * @author Vova Feldman (@svovaf) * @since 1.0.1 */ private function override_plugin_menu_with_activation() { $this->_logger->entrance(); $hook = false; if ( ! $this->has_settings_menu() ) { // Add the opt-in page without a menu item. $hook = FS_Admin_Menu_Manager::add_subpage( '', $this->get_plugin_name(), $this->get_plugin_name(), 'manage_options', $this->_slug, array( &$this, '_connect_page_render' ) ); } else if ( $this->_menu->is_top_level() ) { if ( $this->_menu->is_override_exact() ) { // Make sure the current page is matching the activation page. if ( ! $this->is_matching_url( $this->get_activation_url() ) ) { return; } } $hook = $this->_menu->override_menu_item( array( &$this, '_connect_page_render' ) ); if ( false === $hook ) { // Create new menu item just for the opt-in. $hook = FS_Admin_Menu_Manager::add_page( $this->get_plugin_name(), $this->get_plugin_name(), 'manage_options', $this->_menu->get_slug(), array( &$this, '_connect_page_render' ) ); } } else { $menus = array( $this->_menu->get_parent_slug() ); if ( $this->_menu->is_override_exact() ) { // Make sure the current page is matching the activation page. if ( ! $this->is_matching_url( $this->get_activation_url() ) ) { return; } } foreach ( $menus as $parent_slug ) { $hook = $this->_menu->override_submenu_action( $parent_slug, $this->_menu->get_raw_slug(), array( &$this, '_connect_page_render' ) ); if ( false !== $hook ) { // Found plugin's submenu item. break; } } } if ( $this->is_activation_page() ) { // Clean admin page from distracting content. self::_clean_admin_content_section(); } if ( false !== $hook ) { if ( fs_request_is_action( $this->get_unique_affix() . '_activate_existing' ) ) { $this->_install_with_current_user(); } else if ( fs_request_is_action( $this->get_unique_affix() . '_activate_new' ) ) { $this->_install_with_new_user(); } } } /** * If a plugin was network activated and connected but don't have a network * level settings, then add an artificial menu item for the Account and other * Freemius settings. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ private function add_network_menu_when_missing() { $this->_logger->entrance(); if ( ! $this->_is_network_active ) { // Plugin wasn't activated on the network level. return; } if ( ! fs_is_network_admin() ) { // The context is not the network admin. return; } if ( $this->_menu->has_network_menu() ) { // Plugin already has a network level menu. return; } if ( $this->is_network_activation_mode() ) { /** * Do not add during activation mode, otherwise, there will be duplicate menus while the opt-in * screen is being shown. * * @author Leo Fajardo (@leorw) */ return; } if ( ! WP_FS__SHOW_NETWORK_EVEN_WHEN_DELEGATED ) { if ( $this->is_network_delegated_connection() ) { // Super-admin delegated the connection to the site admins. return; } } if ( ! $this->_menu->has_menu() || $this->_menu->is_top_level() ) { if ( $this->_menu->has_menu() || ! $this->is_addon() || $this->is_activation_mode() ) { $this->_dynamically_added_top_level_page_hook_name = $this->_menu->add_page_and_update( $this->get_plugin_name(), $this->get_plugin_name(), 'manage_options', $this->_menu->has_menu() ? $this->_menu->get_slug() : $this->_slug ); } } else { $this->_menu->add_subpage_and_update( $this->_menu->get_parent_slug(), $this->get_plugin_name(), $this->get_plugin_name(), 'manage_options', $this->_menu->get_slug() ); } } /** * @author Leo Fajardo (@leorw) * @since 1.2.1 * * return string */ function get_top_level_menu_capability() { global $menu; $top_level_menu_slug = $this->get_top_level_menu_slug(); foreach ( $menu as $menu_info ) { /** * The second element in the menu info array is the capability/role that has access to the menu and the * third element is the menu slug. */ if ( $menu_info[2] === $top_level_menu_slug ) { return $menu_info[1]; } } return 'read'; } /** * @author Vova Feldman (@svovaf) * @since 1.0.0 * * @return string */ private function get_top_level_menu_slug() { return ( $this->is_addon() ? $this->get_parent_instance()->_menu->get_top_level_menu_slug() : $this->_menu->get_top_level_menu_slug() ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return string */ function get_pricing_cta_label() { $label = $this->get_text_inline( 'Upgrade', 'upgrade' ); if ( $this->is_in_trial_promotion() && ! $this->is_paying_or_trial() ) { // If running a trial promotion, modify the pricing to load the trial. $label = $this->get_text_inline( 'Start Trial', 'start-trial' ); } else if ( $this->is_paying() ) { $label = $this->get_text_inline( 'Pricing', 'pricing' ); } return $label; } /** * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool */ function is_pricing_page_visible() { return ( // Has at least one paid plan. $this->has_paid_plan() && // Didn't ask to hide the pricing page. $this->is_page_visible( 'pricing' ) && // Don't have a valid active license or has more than one plan. ( ! $this->is_paying() || ! $this->is_single_plan( true ) ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param bool $is_activation_mode * * @return bool */ private function should_add_submenu_or_action_links( $is_activation_mode ) { if ( $this->is_addon() ) { // No submenu items or action links for add-ons. return false; } if ( $this->show_opt_in_on_themes_page() ) { if ( ! fs_is_network_admin() ) { // Also add action links or submenu items when running in a free .org theme so the tabs will be visible. return true; } } else if ( $is_activation_mode ) { // Don't show submenu-items/tabs in activation mode, unless it's a wp.org theme. return false; } if ( fs_is_network_admin() ) { /** * Add submenu items or action links to network level when plugin was network activated and the super * admin did NOT delegate the connection of all sites to site admins. */ return ( $this->_is_network_active && ( WP_FS__SHOW_NETWORK_EVEN_WHEN_DELEGATED || ! $this->is_network_delegated_connection() ) ); } return ( ! $this->_is_network_active || $this->is_delegated_connection() ); } /** * Add default Freemius menu items. * * @author Vova Feldman (@svovaf) * @since 1.0.0 * @since 1.2.2.7 Also add submenu items when running in a free .org theme so the tabs will be visible. */ private function add_submenu_items() { $this->_logger->entrance(); $is_activation_mode = $this->is_activation_mode(); $add_submenu_items = $this->should_add_submenu_or_action_links( $is_activation_mode ); if ( $add_submenu_items ) { if ( $this->has_affiliate_program() ) { // Add affiliation page. $this->add_submenu_item( $this->get_text_inline( 'Affiliation', 'affiliation' ), array( &$this, '_affiliation_page_render' ), $this->get_plugin_name() . ' – ' . $this->get_text_inline( 'Affiliation', 'affiliation' ), 'manage_options', 'affiliation', 'Freemius::_clean_admin_content_section', WP_FS__DEFAULT_PRIORITY, $this->is_submenu_item_visible( 'affiliation' ) ); } } if ( $add_submenu_items || ( $is_activation_mode && $this->is_only_premium() && $this->is_admin_page( 'account' ) && fs_request_is_action( $this->get_unique_affix() . '_sync_license' ) ) ) { if ( ! WP_FS__DEMO_MODE && $this->is_registered() ) { $show_account = ( $this->is_submenu_item_visible( 'account' ) && /** * @since 1.2.2.7 Don't show the Account for free WP.org themes without any paid plans. */ ( ! $this->is_free_wp_org_theme() || $this->has_paid_plan() ) ); // Add user account page. $this->add_submenu_item( $this->get_text_inline( 'Account', 'account' ), array( &$this, '_account_page_render' ), $this->get_plugin_name() . ' – ' . $this->get_text_inline( 'Account', 'account' ), 'manage_options', 'account', array( &$this, '_account_page_load' ), WP_FS__DEFAULT_PRIORITY, ( $add_submenu_items && $show_account ) ); } } if ( $add_submenu_items ) { if (! WP_FS__DEMO_MODE && ! $this->is_whitelabeled() ) { // Add contact page. if ( $this->is_premium() ) { $this->add_submenu_item( $this->get_text_inline( 'Contact Us', 'contact-us' ), array( &$this, '_contact_page_render' ), $this->get_plugin_name() . ' – ' . $this->get_text_inline( 'Contact Us', 'contact-us' ), 'manage_options', 'contact', 'Freemius::_clean_admin_content_section', WP_FS__DEFAULT_PRIORITY, $this->is_submenu_item_visible( 'contact' ) ); } else { $this->add_submenu_link_item( $this->get_text_inline( 'Contact Us', 'contact-us' ), FS_Contact_Form_Manager::instance()->get_standalone_link( $this ), 'contact', 'manage_options', WP_FS__DEFAULT_PRIORITY, $this->is_submenu_item_visible( 'contact' ), 'fs_external_contact', true ); } } if ( $this->has_addons() ) { $this->add_submenu_item( $this->get_text_inline( 'Add-Ons', 'add-ons' ), array( &$this, '_addons_page_render' ), $this->get_plugin_name() . ' – ' . $this->get_text_inline( 'Add-Ons', 'add-ons' ), 'manage_options', 'addons', array( &$this, '_addons_page_load' ), WP_FS__LOWEST_PRIORITY - 1, $this->is_submenu_item_visible( 'addons' ) ); } } if ( $add_submenu_items || ( $is_activation_mode && $this->is_only_premium() && $this->is_admin_page( 'pricing' ) ) ) { if (! WP_FS__DEMO_MODE && ! $this->is_whitelabeled() ) { $show_pricing = ( $this->is_submenu_item_visible( 'pricing' ) && $this->is_pricing_page_visible() ); $pricing_cta_text = $this->get_pricing_cta_label(); $pricing_class = 'upgrade-mode'; if ( $show_pricing ) { if ( $this->is_in_trial_promotion() && ! $this->is_paying_or_trial() ) { // If running a trial promotion, modify the pricing to load the trial. $pricing_class = 'trial-mode'; } else if ( $this->is_paying() ) { $pricing_class = ''; } } $custom_pricing_url = $this->get_pricing_url_with_filter( null ); $pricing_menu_title = $pricing_cta_text . '  ' . ( is_rtl() ? $this->get_text_x_inline( '←', 'ASCII arrow left icon', 'symbol_arrow-left' ) : $this->get_text_x_inline( '➤', 'ASCII arrow right icon', 'symbol_arrow-right' ) ); $show_pricing_submenu_item = ( $add_submenu_items && $show_pricing ); // Add upgrade/pricing submenu item. if ( ! is_null( $custom_pricing_url ) ) { $this->add_submenu_link_item( $pricing_menu_title, $custom_pricing_url, 'pricing', 'manage_options', WP_FS__LOWEST_PRIORITY, $show_pricing_submenu_item, $pricing_class ); } else { $this->add_submenu_item( $pricing_menu_title, array( &$this, '_pricing_page_render' ), $this->get_plugin_name() . ' – ' . $this->get_text_x_inline( 'Pricing', 'noun', 'pricing' ), 'manage_options', 'pricing', 'Freemius::_clean_admin_content_section', WP_FS__LOWEST_PRIORITY, $show_pricing_submenu_item, $pricing_class ); } } } if ( ! $is_activation_mode || ( true !== $this->_storage->require_license_activation ) ) { /** * Add the other menu items if there are any when not in activation mode or license activation is not * required (license activation is required for registered or anonymous users after activating the * premium version when the site is not in trial mode or there's no active valid license). * * @author Leo Fajardo (@leorw) * @since 2.2.1 */ if ( 0 < count( $this->_menu_items ) ) { if ( ! $this->_menu->is_top_level() ) { fs_enqueue_local_style( 'fs_common', '/admin/common.css' ); // Append submenu items right after the plugin's submenu item. $this->order_sub_submenu_items(); } else { // Append submenu items. $this->embed_submenu_items(); } } } } /** * Moved the actual submenu item additions to a separated function, * in order to support sub-submenu items when the plugin's settings * only have a submenu and not top-level menu item. * * @author Vova Feldman (@svovaf) * @since 1.1.4 */ private function embed_submenu_items() { $item_classes = $this->_menu->is_top_level() ? 'fs-submenu-item' : 'fs-submenu-item fs-sub'; $item_template = '%4$s'; $top_level_menu_capability = $this->get_top_level_menu_capability(); ksort( $this->_menu_items ); $is_first_submenu_item = true; foreach ( $this->_menu_items as $priority => $items ) { foreach ( $items as $item ) { $capability = ( ! empty( $item['capability'] ) ? $item['capability'] : $top_level_menu_capability ); $menu_item = sprintf( $item_template, $this->get_unique_affix(), $item['menu_slug'], ! empty( $item['class'] ) ? $item['class'] : '', $item['menu_title'], esc_attr( isset( $item['url'] ) ? $item['url'] : '' ), esc_attr( isset( $item['new_tab'] ) ? 'true' : 'false' ) ); $top_level_menu_slug = $this->get_top_level_menu_slug(); $menu_slug = $this->_menu->get_slug( $item['menu_slug'] ); if ( ! isset( $item['url'] ) ) { $hook = FS_Admin_Menu_Manager::add_subpage( $item['show_submenu'] ? $top_level_menu_slug : '', $item['page_title'], $menu_item, $capability, $menu_slug, $item['render_function'] ); if ( false !== $item['before_render_function'] ) { add_action( "load-$hook", $item['before_render_function'] ); } } else { FS_Admin_Menu_Manager::add_subpage( $item['show_submenu'] ? $top_level_menu_slug : '', $item['page_title'], $menu_item, $capability, $menu_slug, array( $this, '' ) ); } if ( $item['show_submenu'] && $is_first_submenu_item ) { if ( $this->_is_network_active && ! empty( $this->_dynamically_added_top_level_page_hook_name ) ) { /** * If the top-level menu has been dynamically created, remove the first submenu item that * WordPress automatically creates when there's no submenu item whose slug matches the * parent's. In the following example, the `Awesome Plugin` submenu item will be removed. * * Awesome Plugin * - Awesome Plugin <-- we want to remove this since there's no real setting page for the top-level * * @author Leo Fajardo (@leorw) */ remove_submenu_page( $top_level_menu_slug, $top_level_menu_slug ); } $is_first_submenu_item = false; } } } } /** * Re-order the submenu items so all Freemius added new submenu items * are added right after the plugin's settings submenu item. * * @author Vova Feldman (@svovaf) * @since 1.1.4 */ private function order_sub_submenu_items() { global $submenu; $menu_slug = $this->_menu->get_top_level_menu_slug(); /** * Before "admin_menu" fires, WordPress will loop over the default submenus and remove pages for which the user * does not have permissions. So in case a plugin does not have top-level menu but does have submenus under any * of the default menus, only users that have the right role can access its sub-submenus (Account, Contact Us, * Support Forum, etc.) since $submenu[ $menu_slug ] will be empty if the user doesn't have permission. * * In case a plugin does not have submenus under any of the default menus but does have submenus under the menu * of another plugin, only users that have the right role can access its sub-submenus since we will use the * capability needed to access the parent menu as the capability for the submenus that we will add. */ if ( empty( $submenu[ $menu_slug ] ) ) { return; } $top_level_menu = &$submenu[ $menu_slug ]; $all_submenu_items_after = array(); $found_submenu_item = false; foreach ( $top_level_menu as $submenu_id => $meta ) { if ( $found_submenu_item ) { // Remove all submenu items after the plugin's submenu item. $all_submenu_items_after[] = $meta; unset( $top_level_menu[ $submenu_id ] ); } if ( $this->_menu->get_raw_slug() === $meta[2] ) { // Found the submenu item, put all below. $found_submenu_item = true; continue; } } // Embed all plugin's new submenu items. $this->embed_submenu_items(); // Start with specially high number to make sure it's appended. $i = max( 10000, max( array_keys( $top_level_menu ) ) + 1 ); foreach ( $all_submenu_items_after as $meta ) { $top_level_menu[ $i ] = $meta; $i ++; } // Sort submenu items. ksort( $top_level_menu ); } /** * Helper method to return the module's support forum URL. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return string */ function get_support_forum_url() { return $this->apply_filters( 'support_forum_url', "https://wordpress.org/support/{$this->_module_type}/{$this->_slug}" ); } /** * Displays the Support Forum link when enabled. * * Can be filtered like so: * * function _fs_show_support_menu( $is_visible, $menu_id ) { * if ( 'support' === $menu_id ) { * return _fs->is_registered(); * } * return $is_visible; * } * _fs()->add_filter('is_submenu_visible', '_fs_show_support_menu', 10, 2); * */ function _add_default_submenu_items() { if ( ! $this->is_on() ) { return; } if ( ! $this->is_activation_mode() && ( ( $this->_is_network_active && fs_is_network_admin() ) || ( ! $this->_is_network_active && is_admin() ) ) ) { $this->add_submenu_link_item( $this->apply_filters( 'support_forum_submenu', $this->get_text_inline( 'Support Forum', 'support-forum' ) ), $this->get_support_forum_url(), 'wp-support-forum', null, 50, $this->is_submenu_item_visible( 'support' ), '', true ); } } /** * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param string $menu_title * @param callable $render_function * @param bool|string $page_title * @param string $capability * @param bool|string $menu_slug * @param bool|callable $before_render_function * @param int $priority * @param bool $show_submenu * @param string $class Since 1.2.1.5 can add custom classes to menu items. */ function add_submenu_item( $menu_title, $render_function, $page_title = false, $capability = 'manage_options', $menu_slug = false, $before_render_function = false, $priority = WP_FS__DEFAULT_PRIORITY, $show_submenu = true, $class = '' ) { $this->_logger->entrance( 'Title = ' . $menu_title ); if ( $this->is_addon() ) { $parent_fs = $this->get_parent_instance(); if ( is_object( $parent_fs ) ) { $parent_fs->add_submenu_item( $menu_title, $render_function, $page_title, $capability, $menu_slug, $before_render_function, $priority, $show_submenu, $class ); return; } } if ( ! isset( $this->_menu_items[ $priority ] ) ) { $this->_menu_items[ $priority ] = array(); } $this->_menu_items[ $priority ][] = array( 'page_title' => is_string( $page_title ) ? $page_title : $menu_title, 'menu_title' => $menu_title, 'capability' => $capability, 'menu_slug' => is_string( $menu_slug ) ? $menu_slug : strtolower( $menu_title ), 'render_function' => $render_function, 'before_render_function' => $before_render_function, 'show_submenu' => $show_submenu, 'class' => $class, ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param string $menu_title * @param string $url * @param bool $menu_slug * @param string $capability * @param int $priority * @param bool $show_submenu * @param string $class * @param bool $new_tab */ function add_submenu_link_item( $menu_title, $url, $menu_slug = false, $capability = 'read', $priority = WP_FS__DEFAULT_PRIORITY, $show_submenu = true, $class = '', $new_tab = false ) { $this->_logger->entrance( 'Title = ' . $menu_title . '; Url = ' . $url ); if ( $this->is_addon() ) { $parent_fs = $this->get_parent_instance(); if ( is_object( $parent_fs ) ) { $parent_fs->add_submenu_link_item( $menu_title, $url, $menu_slug, $capability, $priority, $show_submenu, $class, $new_tab ); return; } } if ( ! isset( $this->_menu_items[ $priority ] ) ) { $this->_menu_items[ $priority ] = array(); } $this->_menu_items[ $priority ][] = array( 'menu_title' => $menu_title, 'capability' => $capability, 'menu_slug' => is_string( $menu_slug ) ? $menu_slug : strtolower( $menu_title ), 'url' => $url, 'page_title' => $menu_title, 'render_function' => 'fs_dummy', 'before_render_function' => '', 'show_submenu' => $show_submenu, 'class' => $class, 'new_tab' => $new_tab, ); } #endregion ------------------------------------------------------------------ #-------------------------------------------------------------------------------- #region Admin Notices #-------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @param string|string[] $ids * @param int|null $network_level_or_blog_id * * @uses FS_Admin_Notices::remove_sticky() */ function remove_sticky( $ids, $network_level_or_blog_id = null ) { $this->_admin_notices->remove_sticky( $ids, $network_level_or_blog_id ); } #endregion #-------------------------------------------------------------------------------- #region Actions / Hooks / Filters #-------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @param string $tag * * @return string */ public function get_action_tag( $tag ) { return self::get_action_tag_static( $tag, $this->_slug, $this->is_plugin() ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @param string $tag * @param string $slug * @param bool $is_plugin * * @return string */ static function get_action_tag_static( $tag, $slug = '', $is_plugin = true ) { $action = "fs_{$tag}"; if ( ! empty( $slug ) ) { $action .= '_' . self::get_module_unique_affix( $slug, $is_plugin ); } return $action; } /** * Returns a string that can be used to generate a unique action name, * option name, HTML element ID, or HTML element class. * * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return string */ public function get_unique_affix() { return self::get_module_unique_affix( $this->_slug, $this->is_plugin() ); } /** * Returns a string that can be used to generate a unique action name, * option name, HTML element ID, or HTML element class. * * @author Vova Feldman (@svovaf) * @since 1.2.2.5 * * @param string $slug * @param bool $is_plugin * * @return string */ static function get_module_unique_affix( $slug, $is_plugin = true ) { $affix = $slug; if ( ! $is_plugin ) { $affix .= '-' . WP_FS__MODULE_TYPE_THEME; } return $affix; } /** * @author Vova Feldman (@svovaf) * @since 1.2.1 * @since 1.2.2.5 The AJAX action names are based on the module ID, not like the non-AJAX actions that are * based on the slug for backward compatibility. * * @param string $tag * * @return string */ function get_ajax_action( $tag ) { return self::get_ajax_action_static( $tag, $this->_module_id ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $tag * * @return string */ function get_ajax_security( $tag ) { return wp_create_nonce( $this->get_ajax_action( $tag ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $tag */ function check_ajax_referer( $tag ) { check_ajax_referer( $this->get_ajax_action( $tag ), 'security' ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * @since 1.2.2.5 The AJAX action names are based on the module ID, not like the non-AJAX actions that are * based on the slug for backward compatibility. * * @param string $tag * @param number|null $module_id * * @return string */ static function get_ajax_action_static( $tag, $module_id = null ) { $action = "fs_{$tag}"; if ( ! empty( $module_id ) ) { $action .= "_{$module_id}"; } return $action; } /** * Do action, specific for the current context plugin. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param string $tag The name of the action to be executed. * @param mixed $arg,... Optional. Additional arguments which are passed on to the * functions hooked to the action. Default empty. * * @uses do_action() */ function do_action( $tag, $arg = '' ) { $args = func_get_args(); $this->_logger->entrance( $tag ); call_user_func_array( 'do_action', array_merge( array( $this->get_action_tag( $tag ) ), array_slice( $args, 1 ) ) ); } /** * Add action, specific for the current context plugin. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param string $tag * @param callable $function_to_add * @param int $priority * @param int $accepted_args * * @uses add_action() */ function add_action( $tag, $function_to_add, $priority = WP_FS__DEFAULT_PRIORITY, $accepted_args = 1 ) { $this->_logger->entrance( $tag ); add_action( $this->get_action_tag( $tag ), $function_to_add, $priority, $accepted_args ); } /** * Add AJAX action, specific for the current context plugin. * * @author Vova Feldman (@svovaf) * @since 1.2.1 * * @param string $tag * @param callable $function_to_add * @param int $priority * * @uses add_action() * * @return bool True if action added, false if no need to add the action since the AJAX call isn't matching. */ function add_ajax_action( $tag, $function_to_add, $priority = WP_FS__DEFAULT_PRIORITY ) { $this->_logger->entrance( $tag ); return self::add_ajax_action_static( $tag, $function_to_add, $priority, $this->_module_id ); } /** * Add AJAX action. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @param string $tag * @param callable $function_to_add * @param int $priority * @param number|null $module_id * * @return bool True if action added, false if no need to add the action since the AJAX call isn't matching. * @uses add_action() * */ static function add_ajax_action_static( $tag, $function_to_add, $priority = WP_FS__DEFAULT_PRIORITY, $module_id = null ) { self::$_static_logger->entrance( $tag ); if ( ! self::is_ajax_action_static( $tag, $module_id ) ) { return false; } add_action( 'wp_ajax_' . self::get_ajax_action_static( $tag, $module_id ), $function_to_add, $priority, 0 ); self::$_static_logger->info( "$tag AJAX callback action added." ); return true; } /** * Send a JSON response back to an Ajax request. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $response */ static function shoot_ajax_response( $response ) { wp_send_json( $response ); } /** * Send a JSON response back to an Ajax request, indicating success. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $data Data to encode as JSON, then print and exit. */ static function shoot_ajax_success( $data = null ) { wp_send_json_success( $data ); } /** * Send a JSON response back to an Ajax request, indicating failure. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $error Optional error message. */ static function shoot_ajax_failure( $error = '' ) { $result = array( 'success' => false ); if ( ! empty( $error ) ) { $result['error'] = $error; } wp_send_json( $result ); } /** * Returns an AJAX URL with a special extra param to indicate whether the request was triggered from the network admin or blog admin. * * @author Vova Feldman (@svovaf) * @since 2.5.1 * * @param string $wrap_with By default, returns the AJAX URL wrapped with single quotes. * * @return string */ static function ajax_url( $wrap_with = "'") { if ( fs_is_network_admin() ) { $param_name = '_fs_network_admin'; } else { $param_name = '_fs_blog_admin'; } $url = admin_url( 'admin-ajax.php', 'relative' ); $url .= ( false === strpos( $url, '?' ) ) ? '?' : '&'; $url .= "{$param_name}=true"; return "{$wrap_with}{$url}{$wrap_with}"; } /** * Apply filter, specific for the current context plugin. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string $tag The name of the filter hook. * @param mixed $value The value on which the filters hooked to `$tag` are applied on. * * @return mixed The filtered value after all hooked functions are applied to it. * * @uses apply_filters() */ function apply_filters( $tag, $value ) { $args = func_get_args(); $this->_logger->entrance( $tag ); array_unshift( $args, $this->get_unique_affix() ); return call_user_func_array( 'fs_apply_filter', $args ); } /** * Add filter, specific for the current context plugin. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string $tag * @param callable $function_to_add * @param int $priority * @param int $accepted_args * * @uses add_filter() */ function add_filter( $tag, $function_to_add, $priority = WP_FS__DEFAULT_PRIORITY, $accepted_args = 1 ) { $this->_logger->entrance( $tag ); add_filter( $this->get_action_tag( $tag ), $function_to_add, $priority, $accepted_args ); } /** * Check if has filter. * * @author Vova Feldman (@svovaf) * @since 1.1.4 * * @param string $tag * @param callable|bool $function_to_check Optional. The callback to check for. Default false. * * @return false|int * * @uses has_filter() */ function has_filter( $tag, $function_to_check = false ) { $this->_logger->entrance( $tag ); return has_filter( $this->get_action_tag( $tag ), $function_to_check ); } #endregion /** * Override default i18n text phrases. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string[] string $key_value * * @uses fs_override_i18n() */ function override_i18n( $key_value ) { fs_override_i18n( $key_value, $this->_slug ); } /* Account Page ------------------------------------------------------------------------------------------------------------------*/ /** * Update site information. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param bool $store Flush to Database if true. * @param null|int $network_level_or_blog_id Since 2.0.0 * @param \FS_Site $site Since 2.0.0 */ private function _store_site( $store = true, $network_level_or_blog_id = null, FS_Site $site = null, $is_backup = false ) { $this->_logger->entrance(); if ( is_null( $site ) ) { $site = $this->_site; } if ( !isset( $site ) || !is_object($site) || empty( $site->id ) ) { $this->_logger->error( "Empty install ID, can't store site." ); return; } $site_clone = clone $site; $sites = self::get_all_sites( $this->_module_type, $network_level_or_blog_id, $is_backup ); if ( ! $is_backup && is_object( $this->_user ) && $this->_user->id != $site->user_id ) { $this->sync_user_by_current_install( $site->user_id ); $prev_stored_user_id = $this->_storage->get( 'prev_user_id', false, $network_level_or_blog_id ); if ( empty( $prev_stored_user_id ) && is_object($this->_user) && $this->_user->id != $site->user_id ) { /** * Store the current user ID as the previous user ID so that the previous user can be used * as the install's owner while the new owner's details are not yet available. * * This will be executed only in the `replica` site. For example, there are 2 sites, namely `original` * and `replica`, then an ownership change was initiated and completed in the `original`, the `replica` * will be using the previous user until it is updated again (e.g.: until the next clone of `original` * into `replica`. * * @author Leo Fajardo (@leorw) */ $this->_storage->store( 'prev_user_id', $sites[ $this->_slug ]->user_id, $network_level_or_blog_id ); } } $sites[ $this->_slug ] = $site_clone; $this->set_account_option( ( $is_backup ? 'prev_' : '' ) . 'sites', $sites, $store, $network_level_or_blog_id ); } /** * Stores the context site in the sites backup storage. This logic is used before deleting the site info so that it can be restored later on if necessary (e.g., if the automatic clone resolution attempt fails). * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ private function back_up_site() { $this->_logger->entrance(); $site_clone = clone $this->_site; $this->_store_site( true, null, $site_clone, true ); } /** * Update plugin's plans information. * * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @param bool $store Flush to Database if true. */ private function _store_plans( $store = true ) { $this->_logger->entrance(); $plans = self::get_all_plans( $this->_module_type ); // Copy plans. $encrypted_plans = array(); for ( $i = 0, $len = count( $this->_plans ); $i < $len; $i ++ ) { $encrypted_plans[] = self::_encrypt_entity( $this->_plans[ $i ] ); } $plans[ $this->_slug ] = $encrypted_plans; $this->set_account_option( 'plans', $plans, $store ); } /** * Update user's plugin licenses. * * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param bool $store * @param number|bool $module_id * @param FS_Plugin_License[] $licenses */ private function _store_licenses( $store = true, $module_id = false, $licenses = array() ) { $this->_logger->entrance(); $all_licenses = self::get_all_licenses(); if ( ! FS_Plugin::is_valid_id( $module_id ) ) { $module_id = $this->_module_id; $user_licenses = is_array( $this->_licenses ) ? $this->_licenses : array(); if ( empty( $user_licenses ) ) { // If the context user doesn't have any license, don't update the licenses collection. return; } $new_user_licenses_map = array(); foreach ( $user_licenses as $user_license ) { $new_user_licenses_map[ $user_license->id ] = $user_license; } self::store_user_id_license_ids_map( array_keys( $new_user_licenses_map ), $this->_module_id, $this->_user->id ); // Update user licenses. $licenses_to_update_count = count( $new_user_licenses_map ); foreach ( $all_licenses[ $module_id ] as $key => $license ) { if ( 0 === $licenses_to_update_count ) { break; } if ( isset( $new_user_licenses_map[ $license->id ] ) ) { // Update license. $all_licenses[ $module_id ][ $key ] = $new_user_licenses_map[ $license->id ]; unset( $new_user_licenses_map[ $license->id ] ); $licenses_to_update_count --; } } if ( ! empty( $new_user_licenses_map ) ) { // Add new licenses. $all_licenses[ $module_id ] = array_merge( array_values( $new_user_licenses_map ), $all_licenses[ $module_id ] ); } $licenses = $all_licenses[ $module_id ]; } if ( ! isset( $all_licenses[ $module_id ] ) ) { $all_licenses[ $module_id ] = array(); } $all_licenses[ $module_id ] = $licenses; self::$_accounts->set_option( 'all_licenses', $all_licenses, $store ); } /** * Update user information. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param bool $store Flush to Database if true. */ private function _store_user( $store = true ) { $this->_logger->entrance(); if ( empty( $this->_user->id ) ) { $this->_logger->error( "Empty user ID, can't store user." ); return; } $users = self::get_all_users(); $users[ $this->_user->id ] = $this->_user; self::$_accounts->set_option( 'users', $users, $store ); } /** * Update new updates information. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param FS_Plugin_Tag|null $update * @param bool $store Flush to Database if true. * @param bool|number $plugin_id */ private function _store_update( $update, $store = true, $plugin_id = false ) { $this->_logger->entrance(); if ( $update instanceof FS_Plugin_Tag ) { $update->updated = time(); } if ( ! is_numeric( $plugin_id ) ) { $plugin_id = $this->_plugin->id; } $updates = self::get_all_updates(); $updates[ $plugin_id ] = $update; self::$_accounts->set_option( 'updates', $updates, $store ); } /** * Update new updates information. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param FS_Plugin[] $plugin_addons * @param bool $store Flush to Database if true. */ private function _store_addons( $plugin_addons, $store = true ) { $this->_logger->entrance(); $addons = self::get_all_addons(); $addons[ $this->_plugin->id ] = $plugin_addons; self::$_accounts->set_option( 'addons', $addons, $store ); } /** * Delete plugin's associated add-ons. * * @author Vova Feldman (@svovaf) * @since 1.0.8 * * @param bool $store * * @return bool */ private function _delete_account_addons( $store = true ) { $all_addons = self::get_all_account_addons(); if ( ! isset( $all_addons[ $this->_plugin->id ] ) ) { return false; } unset( $all_addons[ $this->_plugin->id ] ); self::$_accounts->set_option( 'account_addons', $all_addons, $store ); return true; } /** * Update account add-ons list. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param FS_Plugin[] $addons * @param bool $store Flush to Database if true. */ private function _store_account_addons( $addons, $store = true ) { $this->_logger->entrance(); $all_addons = self::get_all_account_addons(); $all_addons[ $this->_plugin->id ] = $addons; self::$_accounts->set_option( 'account_addons', $all_addons, $store ); } /** * Purges the cache for the valid user licenses API call so that when the `Account` or `Add-Ons` page is loaded, * the valid user licenses will be fetched again and the account add-ons may be updated. * * @author Leo Fajardo (@leorw) * @since 2.2.4 */ private function purge_valid_user_licenses_cache() { if ( ! $this->is_registered() ) { return; } $this->get_api_user_scope()->purge_cache( $this->get_valid_user_licenses_endpoint() ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @param array $all_licenses * @param number|null $site_license_id * @param bool $include_parent_licenses * * @return array */ private function get_foreign_licenses_info( $all_licenses, $site_license_id = null, $include_parent_licenses = false ) { $foreign_licenses = array( 'ids' => array(), 'license_keys' => array() ); $parent_license_ids_map = array(); foreach ( $all_licenses as $license ) { if ( $license->user_id == $this->_user->id || $license->id == $site_license_id ) { continue; } $foreign_licenses['ids'][] = $license->id; $foreign_licenses['license_keys'][] = $license->secret_key; if ( $include_parent_licenses && is_object( $this->_license ) && FS_Plugin_License::is_valid_id( $this->_license->parent_license_id ) && ! isset( $parent_license_ids_map[ $this->_license->parent_license_id ] ) ) { /** * Include the parent license's info only if it has not been included before since child licenses * can have the same parent license. */ $foreign_licenses['ids'][] = $this->_license->parent_license_id; $foreign_licenses['license_keys'][] = $license->secret_key; $parent_license_ids_map[ $this->_license->parent_license_id ] = true; } } if ( empty( $foreign_licenses['ids'] ) ) { $foreign_licenses = array(); } return $foreign_licenses; } /** * @author Leo Fajardo (@leorw) * @since 2.3.0 * * @return string */ private function get_valid_user_licenses_endpoint() { $user_licenses_endpoint = '/licenses.json?type=active' . ( FS_Plugin::is_valid_id( $this->get_bundle_id() ) ? '&is_enriched=true' : '' ); $foreign_licenses = $this->get_foreign_licenses_info( self::get_all_licenses( $this->_module_id ), null, true ); if ( ! empty ( $foreign_licenses ) ) { $foreign_licenses = array( // Prefix with `+` to tell the server to include foreign licenses in the licenses collection. 'ids' => ( urlencode( '+' ) . implode( ',', $foreign_licenses['ids'] ) ), 'license_keys' => implode( ',', array_map( 'urlencode', $foreign_licenses['license_keys'] ) ) ); $user_licenses_endpoint = add_query_arg( $foreign_licenses, $user_licenses_endpoint ); } return $user_licenses_endpoint; } /** * Fetches active licenses that are enriched with product type if there's a context `bundle_id` and bundle * licenses enriched with product IDs if there are any. From the licenses, the `get_updated_account_addons` * method filters out non–add-on product IDs and stores the add-on IDs. * * @author Leo Fajardo (@leorw) * @since 2.2.4 * * @return stdClass[] array */ private function fetch_valid_user_licenses() { $this->_logger->entrance(); $result = $this->get_api_user_scope()->get( $this->get_valid_user_licenses_endpoint() ); if ( ! $this->is_api_result_object( $result, 'licenses' ) || ! is_array( $result->licenses ) ) { return array(); } return $result->licenses; } /** * @author Leo Fajardo (@leorw) * @since 2.2.4 * * @return number[] Account add-on IDs. */ function get_updated_account_addons() { $addons = $this->get_addons(); if ( empty( $addons ) ) { return array(); } $account_addons = $this->get_account_addons(); if ( ! is_array( $account_addons ) ) { $account_addons = array(); } $user_licenses = $this->is_registered() ? $this->fetch_valid_user_licenses() : array(); if ( empty( $user_licenses ) ) { return $account_addons; } $addon_ids = array(); foreach ( $addons as $addon ) { $addon_ids[] = $addon->id; } $license_product_ids = array(); foreach ( $user_licenses as $license ) { if ( isset( $license->plugin_type ) && 'bundle' === $license->plugin_type ) { $license_product_ids = array_merge( $license_product_ids, $license->products ); } else { $license_product_ids[] = $license->plugin_id; } } // Filter out non–add-on IDs. $new_account_addons = array_intersect( $addon_ids, $license_product_ids ); if ( count( $new_account_addons ) !== count( $account_addons ) ) { $this->_store_account_addons( array_unique( $new_account_addons ) ); } return $new_account_addons; } /** * Store account params in the Database. * * @author Vova Feldman (@svovaf) * @since 1.0.1 * * @param null|int $blog_id Since 2.0.0 */ private function _store_account( $blog_id = null ) { $this->_logger->entrance(); $this->_store_site( false, $blog_id ); $this->_store_user( false ); $this->_store_plans( false ); $this->_store_licenses( false ); self::$_accounts->store( $blog_id ); } /** * Sync user's information. * * @author Vova Feldman (@svovaf) * @since 1.0.3 * @uses FS_Api */ private function _handle_account_user_sync() { $this->_logger->entrance(); $api = $this->get_api_user_scope(); // Get user's information. $user = $api->get( '/', true ); if ( isset( $user->id ) ) { $this->_user->first = $user->first; $this->_user->last = $user->last; $this->_user->email = $user->email; $is_menu_item_account_visible = $this->is_submenu_item_visible( 'account' ); if ( $user->is_verified && ( ! isset( $this->_user->is_verified ) || false === $this->_user->is_verified ) ) { $this->_user->is_verified = true; $this->do_action( 'account_email_verified', $user->email ); $this->_admin_notices->add( $this->get_text_inline( 'Your email has been successfully verified - you are AWESOME!', 'email-verified-message' ), $this->get_text_x_inline( 'Right on', 'a positive response', 'right-on' ) . '!', 'success', // Make admin sticky if account menu item is invisible, // since the page will be auto redirected to the plugin's // main settings page, and the non-sticky message // will disappear. ! $is_menu_item_account_visible, 'email_verified' ); } // Flush user details to DB. $this->_store_user(); $this->do_action( 'after_account_user_sync', $user ); /** * If account menu item is hidden, redirect to plugin's main settings page. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @link https://github.com/Freemius/wordpress-sdk/issues/6 */ if ( ! $is_menu_item_account_visible ) { fs_redirect( $this->_get_admin_page_url() ); } } } /** * @author Vova Feldman (@svovaf) * @since 1.0.9 * @uses FS_Api * * @param number|bool $license_id * * @return FS_Subscription|object|bool */ private function _fetch_site_license_subscription( $license_id = false ) { $this->_logger->entrance(); $api = $this->get_api_site_scope(); if ( ! is_numeric( $license_id ) ) { $license_id = FS_Plugin_License::is_valid_id( $this->_license->parent_license_id ) ? $this->_license->parent_license_id : $this->_license->id; } $result = $api->get( "/licenses/{$license_id}/subscriptions.json", true ); return ! isset( $result->error ) ? ( ( is_array( $result->subscriptions ) && 0 < count( $result->subscriptions ) ) ? new FS_Subscription( $result->subscriptions[0] ) : false ) : $result; } /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * @uses FS_Api * * @param number|bool $plan_id * * @return FS_Plugin_Plan|object */ private function _fetch_site_plan( $plan_id = false ) { $this->_logger->entrance(); $api = $this->get_api_site_scope(); if ( ! is_numeric( $plan_id ) ) { $plan_id = $this->_site->plan_id; } $plan = $api->get( "/plans/{$plan_id}.json", true ); return ! isset( $plan->error ) ? new FS_Plugin_Plan( $plan ) : $plan; } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * @uses FS_Api * * @return FS_Plugin_Plan[]|object */ private function _fetch_plugin_plans() { $this->_logger->entrance(); $api = $this->get_current_or_network_user_api_scope(); /** * @since 1.2.3 When running in DEV mode, retrieve pending plans as well. */ $result = $api->get( $this->add_show_pending( "/plugins/{$this->_module_id}/plans.json" ), true ); if ( $this->is_api_result_object( $result, 'plans' ) && is_array( $result->plans ) ) { for ( $i = 0, $len = count( $result->plans ); $i < $len; $i ++ ) { $result->plans[ $i ] = new FS_Plugin_Plan( $result->plans[ $i ] ); } $result = $result->plans; } return $result; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $plan_id * * @return \FS_Plugin_Plan|object */ private function fetch_plan_by_id( $plan_id ) { $this->_logger->entrance(); $api = $this->get_current_or_network_user_api_scope(); $result = $api->get( "/plugins/{$this->_module_id}/plans/{$plan_id}.json", true ); return $this->is_api_result_entity( $result ) ? new FS_Plugin_Plan( $result ) : $result; } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * @uses FS_Api * * @param number|bool $plugin_id * @param number|bool $site_license_id * @param array $foreign_licenses @since 2.0.0. This is used by network-activated plugins. * @param number|null $blog_id * * @return FS_Plugin_License[]|object */ private function _fetch_licenses( $plugin_id = false, $site_license_id = false, $foreign_licenses = array(), $blog_id = null ) { $this->_logger->entrance(); $api = $this->get_api_user_scope(); if ( ! is_numeric( $plugin_id ) ) { $plugin_id = $this->_plugin->id; } $user_licenses_endpoint = "/plugins/{$plugin_id}/licenses.json?is_enriched=true"; if ( ! empty ( $foreign_licenses ) ) { $foreign_licenses = array( // Prefix with `+` to tell the server to include foreign licenses in the licenses collection. 'ids' => ( urlencode( '+' ) . implode( ',', $foreign_licenses['ids'] ) ), 'license_keys' => implode( ',', array_map( 'urlencode', $foreign_licenses['license_keys'] ) ) ); $user_licenses_endpoint = add_query_arg( $foreign_licenses, $user_licenses_endpoint ); } $result = $api->get( $user_licenses_endpoint, true ); $is_site_license_synced = false; $api_errors = array(); if ( $this->is_api_result_object( $result, 'licenses' ) && is_array( $result->licenses ) ) { for ( $i = 0, $len = count( $result->licenses ); $i < $len; $i ++ ) { $result->licenses[ $i ] = new FS_Plugin_License( $result->licenses[ $i ] ); if ( ( ! $is_site_license_synced ) && is_numeric( $site_license_id ) ) { $is_site_license_synced = ( $site_license_id == $result->licenses[ $i ]->id ); } } $result = $result->licenses; } else { $api_errors[] = $result; $result = array(); } if ( ! $is_site_license_synced ) { if ( ! is_null( $blog_id ) ) { /** * If blog ID is not null, the request is for syncing of the license of a single site via the * network-level "Account" page. * * @author Leo Fajardo (@leorw) */ $this->switch_to_blog( $blog_id ); } $api = $this->get_api_site_scope(); if ( is_numeric( $site_license_id ) ) { // Try to retrieve a foreign license that is linked to the install. $api_result = $api->call( '/licenses.json?is_enriched=true' ); if ( $this->is_api_result_object( $api_result, 'licenses' ) && is_array( $api_result->licenses ) ) { $licenses = $api_result->licenses; if ( ! empty( $licenses ) ) { $result[] = new FS_Plugin_License( $licenses[0] ); } } else { $api_errors[] = $api_result; } } else if ( is_object( $this->_license ) && /** * Sync only if the license belongs to the context plugin. `$plugin_id` can be an add-on ID while * the FS instance that does the syncing is the parent FS instance. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $this->_license->plugin_id == $plugin_id ) { $is_license_in_result = false; if ( ! empty( $result ) ) { foreach ( $result as $license ) { if ( $license->id == $this->_license->id ) { $is_license_in_result = true; break; } } } if ( ! $is_license_in_result ) { // Fetch foreign license by ID and license key. $license = $api->get( "/licenses/{$this->_license->id}.json?license_key=" . urlencode( $this->_license->secret_key ) . '&is_enriched=true' ); if ( $this->is_api_result_entity( $license ) ) { $result[] = new FS_Plugin_License( $license ); } else { $api_errors[] = $license; } } } if ( ! is_null( $blog_id ) ) { $this->switch_to_blog( $this->_storage->network_install_blog_id ); } } if ( is_array( $result ) && 0 < count( $result ) ) { // If found at least one license, return license collection even if there are errors. return $result; } if ( ! empty( $api_errors ) ) { // If found any errors and no licenses, return first error. return $api_errors[0]; } // Fallback to empty licenses list. return $result; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param number $license_id * @param string $license_key * * @return \FS_Plugin_License|object */ private function fetch_license_by_key( $license_id, $license_key ) { $this->_logger->entrance(); $api = $this->get_current_or_network_user_api_scope(); $result = $api->get( "/licenses/{$license_id}.json?license_key=" . urlencode( $license_key ) ); return $this->is_api_result_entity( $result ) ? new FS_Plugin_License( $result ) : $result; } /** * @author Vova Feldman (@svovaf) * @since 1.2.0 * @uses FS_Api * * @param number|bool $plugin_id * @param bool $flush * * @return FS_Payment[]|object */ function _fetch_payments( $plugin_id = false, $flush = false ) { $this->_logger->entrance(); $api = $this->get_api_user_scope(); if ( ! is_numeric( $plugin_id ) ) { $plugin_id = $this->_plugin->id; } $include_bundles = ( is_object( $this->_plugin ) && FS_Plugin::is_valid_id( $this->_plugin->bundle_id ) ); $result = $api->get( "/plugins/{$plugin_id}/payments.json?include_addons=true" . ($include_bundles ? '&include_bundles=true' : ''), $flush ); if ( ! isset( $result->error ) ) { for ( $i = 0, $len = count( $result->payments ); $i < $len; $i ++ ) { $result->payments[ $i ] = new FS_Payment( $result->payments[ $i ] ); } $result = $result->payments; } return $result; } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * @uses FS_Api * * @param bool $flush * * @return \FS_Billing|mixed */ function _fetch_billing( $flush = false ) { require_once WP_FS__DIR_INCLUDES . '/entities/class-fs-billing.php'; $billing = $this->get_api_user_scope()->get( 'billing.json', $flush ); if ( $this->is_api_result_entity( $billing ) ) { $billing = new FS_Billing( $billing ); } return $billing; } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param FS_Plugin_License[] $licenses * @param number $module_id */ private function _update_licenses( $licenses, $module_id ) { $this->_logger->entrance(); if ( is_array( $licenses ) ) { for ( $i = 0, $len = count( $licenses ); $i < $len; $i ++ ) { $licenses[ $i ]->updated = time(); } } $this->_store_licenses( true, $module_id, $licenses ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param bool|number $plugin_id * @param bool $flush Since 1.1.7.3 * @param int $expiration Since 1.2.2.7 * @param bool|string $newer_than Since 2.2.1 * * @return object|false New plugin tag info if exist. */ private function _fetch_newer_version( $plugin_id = false, $flush = true, $expiration = WP_FS__TIME_24_HOURS_IN_SEC, $newer_than = false ) { $latest_tag = $this->_fetch_latest_version( $plugin_id, $flush, $expiration, $newer_than ); if ( ! is_object( $latest_tag ) ) { return false; } $plugin_version = $this->get_plugin_version(); // Check if version is actually newer. $has_new_version = // If it's an non-installed add-on then always return latest. ( $this->_is_addon_id( $plugin_id ) && ! $this->is_addon_activated( $plugin_id ) ) || // Compare versions. version_compare( $plugin_version, $latest_tag->version, '<' ); $this->_logger->departure( $has_new_version ? 'Found newer plugin version ' . $latest_tag->version : 'No new version' ); $is_latest_version_beta = ( 'beta' === $latest_tag->release_mode ); $this->_storage->beta_data = array( 'is_beta' => $is_latest_version_beta, 'version' => $latest_tag->version ); return $has_new_version ? $latest_tag : false; } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param bool|number $plugin_id * @param bool $flush Since 1.1.7.3 * @param int $expiration Since 1.2.2.7 * @param bool|string $newer_than Since 2.2.1 * * @return bool|FS_Plugin_Tag */ function get_update( $plugin_id = false, $flush = true, $expiration = FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION, $newer_than = false ) { $this->_logger->entrance(); if ( ! is_numeric( $plugin_id ) ) { $plugin_id = $this->_plugin->id; } $this->check_updates( true, $plugin_id, $flush, $expiration, $newer_than ); $updates = $this->get_all_updates(); return isset( $updates[ $plugin_id ] ) && is_object( $updates[ $plugin_id ] ) ? $updates[ $plugin_id ] : false; } /** * Check if site assigned with active license. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @deprecated Please use has_active_valid_license() instead because license can be cancelled. */ function has_active_license() { return ( is_object( $this->_license ) && is_numeric( $this->_license->id ) && ! $this->_license->is_expired() ); } /** * Check if site assigned with active & valid (not expired) license. * * @author Vova Feldman (@svovaf) * @since 1.2.1 * * @param bool $check_expiration */ function has_active_valid_license( $check_expiration = true ) { return self::is_active_valid_license( $this->_license, $check_expiration ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 */ function is_data_debug_mode() { if ( is_null( $this->is_whitelabeled ) || ! $this->is_whitelabeled ) { return false; } $fs = $this->is_addon() ? $this->get_parent_instance() : $this; if ( $fs->is_network_active() && fs_is_network_admin() ) { $is_developer_license_debug_mode = get_site_transient( "fs_{$this->get_id()}_data_debug_mode" ); } else { $is_developer_license_debug_mode = get_transient( "fs_{$this->get_id()}_data_debug_mode" ); } return ( 'true' === $is_developer_license_debug_mode ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 */ function _set_data_debug_mode() { if ( ! $this->is_whitelabeled( true ) ) { return; } $license_or_user_key = fs_request_get_raw( 'license_or_user_key' ); $transient_value = ( ! empty( $license_or_user_key ) ) ? 'true' : 'false'; if ( 'true' === $transient_value ) { $stored_key = $this->_storage->get( ! FS_User::is_valid_id( $this->_storage->last_license_user_id ) ? 'last_license_key' : 'last_license_user_key' ); if ( md5( $license_or_user_key ) !== $stored_key ) { $this->shoot_ajax_failure( sprintf( '%s... %s', $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ), $this->get_text_inline( 'seems like the key you entered doesn\'t match our records.', 'developer-or-license-not-found' ) ) ); } } if ( $this->is_network_active() && fs_is_network_admin() ) { set_site_transient( "fs_{$this->get_id()}_data_debug_mode", $transient_value, WP_FS__TIME_24_HOURS_IN_SEC / 24 ); } else { set_transient( "fs_{$this->get_id()}_data_debug_mode", $transient_value, WP_FS__TIME_24_HOURS_IN_SEC / 24 ); } if ( 'true' === $transient_value ) { $this->_admin_notices->add_sticky( $this->get_text_inline( 'Debug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the "Stop Debug" link.', 'data_debug_mode_enabled' ), 'data_debug_mode_enabled' ); } $this->shoot_ajax_success(); } /** * Check if a given license is active & valid (not expired). * * @author Vova Feldman (@svovaf) * @since 2.1.3 * * @param FS_Plugin_License $license * @param bool $check_expiration * * @return bool */ private static function is_active_valid_license( $license, $check_expiration = true ) { return ( is_object( $license ) && FS_Plugin_License::is_valid_id( $license->id ) && $license->is_active() && ( ! $check_expiration || $license->is_valid() ) ); } /** * Checks if there's any site that is associated with an active & valid license. * This logic is used to determine if the admin can download the premium code base from a network level admin. * * @author Vova Feldman (@svovaf) * @since 2.1.3 * * @return bool */ function has_any_active_valid_license() { if ( ! fs_is_network_admin() ) { return $this->has_active_valid_license(); } $installs = $this->get_blog_install_map(); $all_plugin_licenses = self::get_all_licenses( $this->_module_id ); foreach ( $installs as $blog_id => $install ) { if ( ! FS_Plugin_License::is_valid_id( $install->license_id ) ) { continue; } foreach ( $all_plugin_licenses as $license ) { if ( $license->id == $install->license_id ) { if ( self::is_active_valid_license( $license ) ) { return true; } } } } return false; } /** * Check if site assigned with license with enabled features. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool */ function has_features_enabled_license() { return ( is_object( $this->_license ) && is_numeric( $this->_license->id ) && $this->_license->is_features_enabled() ); } /** * Checks if the product is activated with a bundle license. * * @author Leo Fajardo (@leorw) * @since 2.4.0 * * @return bool */ function is_activated_with_bundle_license() { if ( ! $this->has_features_enabled_license() ) { return false; } return FS_Plugin_License::is_valid_id( $this->_license->parent_license_id ); } /** * Check if user is a trial or have feature enabled license. * * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @return bool */ function can_use_premium_code() { return $this->is_trial() || $this->has_features_enabled_license(); } /** * Checks if the current user can activate plugins or switch themes. Note that this method should only be used * after the `init` action is triggered because it is using `current_user_can()` which is only functional after * the context user is authenticated. * * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return bool */ function is_user_admin() { /** * Require a super-admin when network activated, running from the network level OR if * running from the site level but not delegated the opt-in. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ if ( $this->_is_network_active && ( fs_is_network_admin() || ! $this->is_delegated_connection() ) ) { return is_super_admin(); } return ( $this->is_plugin() && current_user_can( is_multisite() ? 'manage_options' : 'activate_plugins' ) ) || ( $this->is_theme() && current_user_can( 'switch_themes' ) ); } /** * Sync site's plan. * * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @uses FS_Api * * @param bool $background Hints the method if it's a background sync. If false, it means that was initiated by * the admin. * @param bool $is_context_single_site @since 2.0.0. This is used when syncing a license for a single install from the * network-level "Account" page. * @param int|null $current_blog_id @since 2.2.3. This is passed from the `execute_cron` method and used by the * `_sync_plugin_license` method in order to switch to the previous blog when sending * updates for a single site in case `execute_cron` has switched to a different blog. */ private function _sync_license( $background = false, $is_context_single_site = false, $current_blog_id = null ) { $this->_logger->entrance(); $plugin_id = fs_request_get( 'plugin_id', $this->get_id() ); $is_addon_sync = ( ! $this->_plugin->is_addon() && $plugin_id != $this->get_id() ); if ( $is_addon_sync ) { $this->_sync_addon_license( $plugin_id, $background ); } else { $this->_sync_plugin_license( $background, true, $is_context_single_site, $current_blog_id ); } $this->do_action( 'after_account_plan_sync', $this->get_plan_name() ); } /** * Sync plugin's add-on license. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * @uses FS_Api * * @param number $addon_id * @param bool $background */ private function _sync_addon_license( $addon_id, $background ) { $this->_logger->entrance(); if ( $this->is_addon_activated( $addon_id ) ) { // If already installed, use add-on sync. $fs_addon = self::get_instance_by_id( $addon_id ); if ( // Add-on is network activated and network integrated. $fs_addon->is_network_active() || // Background sync cron. self::is_cron() || // Add-on is not network activated or not network integrated. ! fs_is_network_admin() ) { $fs_addon->_sync_license( $background ); return; } } // Validate add-on exists. $addon = $this->get_addon( $addon_id ); if ( ! is_object( $addon ) ) { return; } // Add add-on into account add-ons. $account_addons = $this->get_account_addons(); if ( ! is_array( $account_addons ) ) { $account_addons = array(); } $account_addons[] = $addon->id; $account_addons = array_unique( $account_addons ); $this->_store_account_addons( $account_addons ); // Load add-on licenses. $licenses = $this->_fetch_licenses( $addon->id ); // Sync add-on licenses. if ( $this->is_array_instanceof( $licenses, 'FS_Plugin_License' ) ) { $this->_update_licenses( $licenses, $addon->id ); if ( ! $this->is_addon_installed( $addon->id ) && FS_License_Manager::has_premium_license( $licenses ) ) { $plans_result = $this->get_api_site_or_plugin_scope()->get( $this->add_show_pending( "/addons/{$addon_id}/plans.json" ) ); if ( ! isset( $plans_result->error ) ) { $plans = array(); foreach ( $plans_result->plans as $plan ) { $plans[] = new FS_Plugin_Plan( $plan ); } $this->_admin_notices->add_sticky( sprintf( ( FS_Plan_Manager::instance()->has_free_plan( $plans ) ? $this->get_text_inline( 'Your %s Add-on plan was successfully upgraded.', 'addon-successfully-upgraded-message' ) : /* translators: %s:product name, e.g. Facebook add-on was successfully... */ $this->get_text_inline( '%s Add-on was successfully purchased.', 'addon-successfully-purchased-message' ) ), $addon->title ) . ' ' . $this->get_latest_download_link( $this->get_text_inline( 'Download the latest version', 'download-latest-version' ), $addon_id ), 'addon_plan_upgraded_' . $addon->slug, $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!' ); } } } } /** * Sync site's plugin plan. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * @uses FS_Api * * @param bool $background Hints the method if it's a background sync. If false, it means that was initiated by the admin. * @param bool $send_installs_update Since 2.0.0 * @param bool $is_context_single_site Since 2.0.0. This is used when sending an update for a single install and * syncing its license from the network-level "Account" page (e.g.: after * activating a license only for the single install). * @param int|null $current_blog_id Since 2.2.3. This is passed from the `execute_cron` method so that it * can be used here to switch to the previous blog in case `execute_cron` * has switched to a different blog. */ private function _sync_plugin_license( $background = false, $send_installs_update = true, $is_context_single_site = false, $current_blog_id = null ) { $this->_logger->entrance(); $plan_change = 'none'; $is_site_level_sync = ( $is_context_single_site || fs_is_blog_admin() || ! $this->_is_network_active ); if ( ! $send_installs_update ) { $site = $this->_site; } else { /** * Sync site info. * * @todo This line will execute install sync on a daily basis, even if running the free version (for opted-in users). The reason we want to keep it that way is for cases when the user was a paying customer, then there was a failure in subscription payment, and then after some time the payment was successful. This could be heavily optimized. For example, we can skip the $flush if the current install was never associated with a paid version. */ if ( $is_site_level_sync ) { /** * Switch to the previous blog since `execute_cron` may have switched to a different blog. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ if ( is_numeric( $current_blog_id ) ) { $this->switch_to_blog( $current_blog_id ); } $result = $this->send_install_update( array(), true, true ); $is_valid = $this->is_api_result_entity( $result ); } else { $result = $this->send_installs_update( array(), true, true ); $is_valid = $this->is_api_result_object( $result, 'installs' ); } if ( ! $is_valid ) { if ( $is_context_single_site ) { // Switch back to the main blog so that the following logic will have the right entities. $this->switch_to_blog( $this->_storage->network_install_blog_id ); } // Show API message only if not background sync or if paying customer. if ( ! $background || $this->is_paying() ) { // Try to ping API to see if not blocked. if ( FS_Api::is_blocked( $result ) ) { /** * @author Vova Feldman (@svovaf) * @since 1.1.6 Only show message related to one of the Freemius powered plugins. Once it will be resolved it will fix the issue for all plugins anyways. There's no point to scare users with multiple error messages. */ if ( ! self::$_global_admin_notices->has_sticky( 'api_blocked' ) ) { // Add notice immediately if not a background sync. $add_notice = ( ! $background ); if ( ! $add_notice ) { $counter = (int) get_transient( '_fs_api_connection_retry_counter' ); // We only want to add the notice after 3 consecutive failures. $add_notice = ( 3 <= $counter ); if ( ! $add_notice ) { /** * Update counter transient only if notice shouldn't be added. If it is added the transient will be reset anyway, because the retries mechanism should only start counting if the admin isn't aware of the connectivity issue. * * Also, since the background sync happens once a day, setting the transient expiration for a week should be enough to count 3 failures, if there's an actual connectivity issue. */ set_transient( '_fs_api_connection_retry_counter', $counter + 1, WP_FS__TIME_WEEK_IN_SEC ); } } // Add notice instantly for not-background sync and only after 3 failed attempts for background sync. if ( $add_notice ) { self::$_global_admin_notices->add( $this->generate_api_blocked_notice_message_from_result( $result ), '', 'error', $background, 'api_blocked' ); add_action( 'admin_footer', array( 'Freemius', '_add_api_connectivity_notice_handler_js' ) ); // Notice was just shown, reset connectivity counter. delete_transient( '_fs_api_connection_retry_counter' ); } } } else if ( is_object( $result ) ) { // Authentication params are broken. $this->_admin_notices->add( $this->get_text_inline( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.', 'wrong-authentication-param-message' ) . '
    ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true ), '', 'error' ); } } // No reason to continue with license sync while there are API issues. return; } // API is working now. Delete the transient and start afresh. delete_transient('_fs_api_connection_retry_counter'); if ( $is_site_level_sync ) { $site = new FS_Site( $result ); } else { // Map site addresses to their blog IDs. $address_to_blog_map = $this->get_address_to_blog_map(); // Find the current context install. $site = null; foreach ( $result->installs as $install ) { if ( $install->id == $this->_site->id ) { $site = new FS_Site( $install ); } else { $address = trailingslashit( fs_strip_url_protocol( $install->url ) ); $blog_id = $address_to_blog_map[ $address ]; $this->_store_site( true, $blog_id, new FS_Site( $install ) ); } } } // Sync plans. $this->_sync_plans(); } // Remove sticky API connectivity message. self::$_global_admin_notices->remove_sticky( 'api_blocked' ); if ( ! $this->has_paid_plan() ) { $this->_site = $site; $this->_store_site( true, $is_site_level_sync ? null : $this->get_network_install_blog_id() ); } else { $context_blog_id = 0; if ( $is_context_single_site ) { $context_blog_id = get_current_blog_id(); // Switch back to the main blog in order to properly sync the license. $this->switch_to_blog( $this->_storage->network_install_blog_id ); } /** * Sync licenses. Pass the site's license ID so that the foreign licenses will be fetched if the license * associated with that ID is not included in the user's licenses collection. * Save previous value to manage remote license renewals. */ $was_license_expired_before_sync = is_object( $this->_license ) && $this->_license->is_expired(); $this->_sync_licenses( $site->license_id, ( $is_context_single_site ? $context_blog_id : null ) ); if ( $is_context_single_site ) { $this->switch_to_blog( $context_blog_id ); } // Check if plan / license changed. if ( $site->plan_id != $this->_site->plan_id || // Check if trial started. $site->trial_plan_id != $this->_site->trial_plan_id || $site->trial_ends != $this->_site->trial_ends || // Check if license changed. $site->license_id != $this->_site->license_id ) { if ( $site->is_trial() && ( ! $this->_site->is_trial() || $site->trial_ends != $this->_site->trial_ends ) ) { // New trial started. $this->_site = $site; $plan_change = 'trial_started'; // For trial with subscription use-case. $new_license = is_null( $site->license_id ) ? null : $this->_get_license_by_id( $site->license_id ); if ( is_object( $new_license ) && $new_license->is_valid() ) { $this->_site = $site; $this->_update_site_license( $new_license ); $this->_store_licenses(); $this->_sync_site_subscription( $this->_license ); } } else if ( $this->_site->is_trial() && ! $site->is_trial() && ! is_numeric( $site->license_id ) ) { // Was in trial, but now trial expired and no license ID. // New trial started. $this->_site = $site; $plan_change = 'trial_expired'; } else { $is_free = $this->is_free_plan(); // Make sure license exist and not expired. $new_license = is_null( $site->license_id ) ? null : $this->_get_license_by_id( $site->license_id ); if ( $is_free && is_null( $new_license ) && $this->has_any_license() && $this->_license->is_cancelled ) { // License cancelled. $this->_site = $site; $this->_update_site_license( $new_license ); $this->_store_licenses(); $plan_change = 'cancelled'; } else if ( $is_free && ( ( ! is_object( $new_license ) || $new_license->is_expired() ) ) ) { // The license is expired, so ignore upgrade method. $this->_site = $site; } else { // License changed. $this->_site = $site; /** * IMPORTANT: * The line below should be executed before trying to activate the license on the rest of the network, otherwise, the license' activation counters may be out of sync + there's no need to activate the license on the context site since it's already activated on it. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ $this->_update_site_license( $new_license ); if ( ! $is_context_single_site && fs_is_network_admin() && $this->_is_network_active && $new_license->quota > 1 && get_blog_count() > 1 ) { // See if license can activated on all sites. if ( ! $this->try_activate_license_on_network( $this->_user, $new_license ) ) { if ( ! fs_request_get_bool( 'auto_install' ) ) { // Open the license activation dialog box on the account page. add_action( 'admin_footer', array( &$this, '_open_license_activation_dialog_box' ) ); } } } $this->_store_licenses(); $plan_change = $is_free ? ( $this->is_only_premium() ? 'activated' : 'upgraded' ) : ( is_object( $new_license ) ? 'changed' : 'downgraded' ); } } // Store updated site info. $this->_store_site( true, $is_site_level_sync ? null : $this->get_network_install_blog_id() ); } else { if ( ! is_object( $this->_license ) ) { $this->maybe_update_whitelabel_flag( FS_Plugin_License::is_valid_id( $site->license_id ) ? $this->get_license_by_id( $site->license_id ) : null ); } else { $this->maybe_update_whitelabel_flag( $this->_license ); if ( $this->_license->is_expired() ) { if ( ! $this->has_features_enabled_license() ) { $this->_deactivate_license(); $plan_change = 'downgraded'; } else { $last_time_expired_license_notice_was_shown = $this->_storage->get( 'expired_license_notice_shown', 0 ); if ( time() - ( 14 * WP_FS__TIME_24_HOURS_IN_SEC ) >= $last_time_expired_license_notice_was_shown ) { /** * Show the expired license notice every 14 days. * * @author Leo Fajardo (@leorw) * @since 2.3.1 */ $plan_change = 'expired'; } } } else if ( $was_license_expired_before_sync ) { /** * If license was expired but it is not anymore. * * * @author Daniele Alessandra (@danielealessandra) */ $plan_change = 'extended'; } } if ( is_numeric( $site->license_id ) && is_object( $this->_license ) ) { $this->_sync_site_subscription( $this->_license ); } } if ( ! $this->is_addon() && $this->_site->is_beta() !== $site->is_beta() ) { // Beta flag updated. $this->_site = $site; $this->_store_site( true, $is_site_level_sync ? null : $this->get_network_install_blog_id() ); } if ( $this->is_addon() || $this->has_addons() ) { /** * Purge the valid user licenses cache so that when the "Account" or the "Add-Ons" page is loaded, * an updated valid user licenses collection will be fetched from the server which is used to also * update the account add-ons (add-ons the user has licenses for). * * @author Leo Fajardo (@leorw) * @since 2.2.4 */ $this->purge_valid_user_licenses_cache(); } } $hmm_text = $this->get_text_x_inline( 'Hmm', 'something somebody says when they are thinking about what you have just said.', 'hmm' ) . '...'; if ( $this->apply_filters( 'has_paid_plan_account', $this->has_paid_plan() ) ) { switch ( $plan_change ) { case 'none': if ( ! $background && is_admin() ) { $plan = $this->is_trial() ? $this->get_trial_plan() : $this->get_plan(); if ( $plan->is_free() ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'It looks like you are still on the %s plan. If you did upgrade or change your plan, it\'s probably an issue on our side - sorry.', 'plan-did-not-change-message' ), '' . $plan->title . ( $this->is_trial() ? ' ' . $this->get_text_x_inline( 'Trial', 'trial period', 'trial' ) : '' ) . '' ) . ' ' . sprintf( '%s', $this->contact_url( 'bug', sprintf( $this->get_text_inline( 'I have upgraded my account but when I try to Sync the License, the plan remains %s.', 'plan-did-not-change-email-message' ), strtoupper( $plan->name ) ) ), $this->get_text_inline( 'Please contact us here', 'contact-us-here' ) ), $hmm_text ); } } break; case 'upgraded': case 'activated': $this->add_after_plan_activation_or_upgrade_instructions_notice( 'upgraded' === $plan_change ); $this->_admin_notices->remove_sticky( array( 'trial_started', 'trial_promotion', 'trial_expired', 'activation_complete', 'license_expired', ) ); break; case 'extended': $this->_admin_notices->remove_sticky( array( 'trial_expired', 'license_expired', ) ); break; case 'changed': $this->_admin_notices->add_sticky( sprintf( $this->get_text_inline( 'Your plan was successfully changed to %s.', 'plan-changed-to-x-message' ), $this->get_plan_title() ), 'plan_changed' ); $this->_admin_notices->remove_sticky( array( 'trial_started', 'trial_promotion', 'trial_expired', 'activation_complete', ) ); break; case 'downgraded': $this->_admin_notices->add_sticky( ($this->has_free_plan() ? sprintf( $this->get_text_inline( 'Your license has expired. You can still continue using the free %s forever.', 'license-expired-blocking-message' ), $this->_module_type ) : /* translators: %1$s: product title; %2$s, %3$s: wrapping HTML anchor element; %4$s: 'plugin', 'theme', or 'add-on'. */ sprintf( $this->get_text_inline( 'Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.', 'license-expired-blocking-message_premium-only' ), sprintf('', $this->pricing_url()), '', $this->get_module_label(true) ) ), 'license_expired', $hmm_text ); $this->_admin_notices->remove_sticky( 'plan_upgraded' ); break; case 'cancelled': $this->_admin_notices->add( $this->get_text_inline( 'Your license has been cancelled. If you think it\'s a mistake, please contact support.', 'license-cancelled' ) . ' ' . sprintf( '%s', $this->contact_url( 'bug' ), $this->get_text_inline( 'Please contact us here', 'contact-us-here' ) ), $hmm_text, 'error' ); $this->_admin_notices->remove_sticky( 'plan_upgraded' ); break; case 'expired': $this->_admin_notices->add_sticky( sprintf( $this->get_text_inline( 'Your license has expired. You can still continue using all the %s features, but you\'ll need to renew your license to continue getting updates and support.', 'license-expired-non-blocking-message' ), $this->get_plan()->title ), 'license_expired', $hmm_text ); $this->_storage->expired_license_notice_shown = WP_FS__SCRIPT_START_TIME; $this->_admin_notices->remove_sticky( 'plan_upgraded' ); break; case 'trial_started': $this->add_complete_upgrade_instructions_notice( sprintf( $this->get_text_inline( 'Your trial has been successfully started.', 'trial-started-message' ), '' . $this->get_plugin_name() . '' ), 'trial_started', $this->get_trial_plan()->title ); $this->_admin_notices->remove_sticky( array( 'trial_promotion', ) ); break; case 'trial_expired': $this->_admin_notices->add_sticky( ($this->has_free_plan() ? $this->get_text_inline( 'Your free trial has expired. You can still continue using all our free features.', 'trial-expired-message' ) : /* translators: %1$s: product title; %2$s, %3$s: wrapping HTML anchor element; %4$s: 'plugin', 'theme', or 'add-on'. */ sprintf( $this->get_text_inline( 'Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.', 'trial-expired-message_premium-only' ), sprintf('', $this->pricing_url()), '', $this->get_module_label(true))), 'trial_expired', $hmm_text ); $this->_admin_notices->remove_sticky( array( 'trial_started', 'trial_promotion', 'plan_upgraded', ) ); break; } } if ( 'none' !== $plan_change ) { if ( ! is_object( $this->_license ) || ! $this->_license->is_whitelabeled ) { $this->_admin_notices->remove_sticky( 'license_whitelabeled' ); } $this->do_action( 'after_license_change', $plan_change, $this->get_plan() ); } } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 * * @param mixed $result * * @return string */ private function generate_api_blocked_notice_message_from_result( $result ) { $api_domains = $this->apply_filters( 'api_domains', array( 'api.freemius.com', 'wp.freemius.com', ) ); $api_domains_list_items = ''; foreach( $api_domains as $api_domain ) { $api_domains_list_items .= "
  • {$api_domain}
  • "; } $error_message = sprintf( $this->get_text_inline( 'Your server is blocking the access to Freemius\' API, which is crucial for %1$s synchronization. Please contact your host to whitelist the following domains:%2$s', 'server-blocking-access' ), $this->get_plugin_name(), "
      {$api_domains_list_items}
    " . $this->get_text_inline( 'Show error details', 'show-error-details' ) . " " ); $error_message = "
    {$error_message}
    " . ''; return $error_message; } /** * Include the required JS at the footer of the admin to trigger the license activation dialog box. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ public function _open_license_activation_dialog_box() { $vars = array( 'license_id' => $this->_site->license_id ); fs_require_once_template( 'js/open-license-activation.php', $vars ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param bool $background * @param FS_Plugin_License|null $premium_license */ protected function _activate_license( $background = false, $premium_license = null ) { $this->_logger->entrance(); if ( is_null( $premium_license ) ) { $license_id = fs_request_get( 'license_id' ); if ( is_object( $this->_site ) && FS_Plugin_License::is_valid_id( $license_id ) && $license_id == $this->_site->license_id ) { // License is already activated. return; } $premium_license = FS_Plugin_License::is_valid_id( $license_id ) ? $this->_get_license_by_id( $license_id ) : $this->_get_available_premium_license(); } if ( ! is_object( $premium_license ) ) { return; } if ( ! is_object( $this->_site ) ) { // Not yet opted-in. $user = $this->get_current_or_network_user(); if ( ! is_object( $user ) ) { $user = self::_get_user_by_id( $premium_license->user_id ); } if ( is_object( $user ) ) { $this->install_with_user( $user, $premium_license->secret_key, false, false, false ); } else { $this->opt_in( false, false, false, $premium_license->secret_key ); return; } } /** * If the premium license is already associated with the install, just * update the license reference (activation is not required). * * @since 1.1.9 */ if ( $premium_license->id == $this->_site->license_id ) { // License is already activated. $this->_update_site_license( $premium_license ); $this->_store_account(); return; } if ( $this->_site->user_id != $premium_license->user_id ) { $api_request_params = array( 'license_key' => $premium_license->secret_key ); } else { $api_request_params = array(); } $api = $this->get_api_site_scope(); $license = $api->call( "/licenses/{$premium_license->id}.json?is_enriched=true", 'put', $api_request_params ); if ( ! $this->is_api_result_entity( $license ) ) { if ( ! $background ) { $this->_admin_notices->add( sprintf( '%s %s', $this->get_text_inline( 'It looks like the license could not be activated.', 'license-activation-failed-message' ), ( is_object( $license ) && isset( $license->error ) ? $license->error->message : sprintf( '%s
    %s', $this->get_text_inline( 'Error received from the server:', 'server-error-message' ), var_export( $license, true ) ) ) ), $this->get_text_x_inline( 'Hmm', 'something somebody says when they are thinking about what you have just said.', 'hmm' ) . '...', 'error' ); } return; } $premium_license = new FS_Plugin_License( $license ); // Updated site plan. $site = $this->get_api_site_scope()->get( '/', true ); if ( $this->is_api_result_entity( $site ) ) { $this->_site = new FS_Site( $site ); } $this->_update_site_license( $premium_license ); $this->_store_account(); if ( $this->is_addon() || $this->has_addons() ) { /** * Purge the valid user licenses cache so that when the "Account" or the "Add-Ons" page is loaded, * an updated valid user licenses collection will be fetched from the server which is used to also * update the account add-ons (add-ons the user has licenses for). * * @author Leo Fajardo (@leorw) * @since 2.2.4 */ $this->purge_valid_user_licenses_cache(); } if ( ! $background ) { $this->add_complete_upgrade_instructions_notice( $this->get_text_inline( 'Your license was successfully activated.', 'license-activated-message' ), 'license_activated' ); } $this->_admin_notices->remove_sticky( array( 'trial_promotion', 'license_expired', ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @param bool $show_notice */ protected function _deactivate_license( $show_notice = true ) { $this->_logger->entrance(); $hmm_text = $this->get_text_x_inline( 'Hmm', 'something somebody says when they are thinking about what you have just said.', 'hmm' ) . '...'; if ( ! FS_Plugin_License::is_valid_id( $this->_site->license_id ) ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'It looks like your site currently doesn\'t have an active license.', 'no-active-license-message' ), $this->get_plan_title() ), $hmm_text ); return; } $api = $this->get_api_site_scope(); $license = $api->call( "/licenses/{$this->_site->license_id}.json", 'delete' ); $this->handle_license_deactivation_result( $license, $hmm_text, $show_notice ); } /** * @author Leo Fajardo (@leorw) * @since 2.2.1 * * @param FS_Plugin_License $license * @param bool|string $hmm_text * @param bool $show_notice */ private function handle_license_deactivation_result( $license, $hmm_text = false, $show_notice = true ) { if ( isset( $license->error ) ) { $this->_admin_notices->add( $this->get_text_inline( 'It looks like the license deactivation failed.', 'license-deactivation-failed-message' ) . '
    ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . ' ' . var_export( $license->error, true ), $hmm_text, 'error' ); return; } // Update license cache. if ( is_array( $this->_licenses ) ) { for ( $i = 0, $len = count( $this->_licenses ); $i < $len; $i ++ ) { if ( $license->id == $this->_licenses[ $i ]->id ) { $this->_licenses[ $i ] = new FS_Plugin_License( $license ); } } } // Update site plan to default. $this->_sync_plans(); $this->_site->plan_id = $this->_plans[0]->id; // Unlink license from site. $this->_update_site_license( null ); $this->_store_account(); if ( $show_notice ) { $this->_admin_notices->add( sprintf( $this->is_only_premium() ? $this->get_text_inline( 'Your %s license was successfully deactivated.', 'license-deactivation-message_premium-only' ) : $this->get_text_inline( 'Your license was successfully deactivated, you are back to the %s plan.', 'license-deactivation-message' ), $this->get_plan_title() ), $this->get_text_inline( 'O.K', 'ok' ) ); } $this->_admin_notices->remove_sticky( array( 'plan_upgraded', 'license_activated', ) ); } /** * Site plan downgrade. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @return object * * @uses FS_Api */ private function _downgrade_site() { $this->_logger->entrance(); $deactivate_license = fs_request_get_bool( 'deactivate_license' ); $api = $this->get_api_site_scope(); $site = $api->call( 'downgrade.json', 'put', array( 'deactivate_license' => $deactivate_license ) ); $plan_downgraded = false; $plan = false; if ( $this->is_api_result_entity( $site ) ) { $prev_plan_id = $this->_site->plan_id; // Update new site plan id. $this->_site->plan_id = $site->plan_id; $plan = $this->get_plan(); $subscription = $this->_sync_site_subscription( $this->_license ); // Plan downgraded if plan was changed or subscription was cancelled. $plan_downgraded = ( $plan instanceof FS_Plugin_Plan && $prev_plan_id != $plan->id ) || ( is_object( $subscription ) && ! isset( $subscription->error ) && ! $subscription->is_active() ); } else { // handle different error cases. $this->handle_license_deactivation_result( $site, $this->get_text_x_inline( 'Hmm', 'something somebody says when they are thinking about what you have just said.', 'hmm' ) . '...' ); } if ( ! $plan_downgraded ) { return (object) array( 'error' => (object) array( 'message' => $this->get_text_inline( 'Seems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes.', 'subscription-cancellation-failure-message' ) ) ); } // Remove previous sticky message about upgrade (if exist). $this->_admin_notices->remove_sticky( 'plan_upgraded' ); $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Your subscription was successfully cancelled. Your %s plan license will expire in %s.', 'plan-x-downgraded-message' ), $plan->title, human_time_diff( time(), strtotime( $this->_license->expiration ) ) ) ); // Store site updates. $this->_store_site(); if ( $deactivate_license && ! FS_Plugin_License::is_valid_id( $site->license_id ) ) { if ( $this->_site->is_localhost() ) { $this->_license->activated_local = max( 0, $this->_license->activated_local - 1 ); } else { $this->_license->activated = max( 0, $this->_license->activated - 1 ); } // Handle successful license deactivation result. $this->handle_license_deactivation_result( $this->_license ); } return $site; } /** * @author Vova Feldman (@svovaf) * @since 1.1.8.1 * * @param bool|string $plan_name * @param bool $add_sticky_notice * * @return bool If trial was successfully started. */ function start_trial( $plan_name = false, $add_sticky_notice = false ) { $this->_logger->entrance(); // Alias. $oops_text = $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...'; if ( $this->is_trial() ) { // Already in trial mode. $this->_admin_notices->add( sprintf( $this->get_text_inline( 'You are already running the %s in a trial mode.', 'in-trial-mode' ), $this->_module_type ), $oops_text, 'error', $add_sticky_notice ); return false; } if ( $this->_site->is_trial_utilized() && ! $this->is_payments_sandbox() ) { // Trial was already utilized. $this->_admin_notices->add( $this->get_text_inline( 'You already utilized a trial before.', 'trial-utilized' ), $oops_text, 'error', $add_sticky_notice ); return false; } if ( false !== $plan_name ) { $plan = $this->get_plan_by_name( $plan_name ); if ( false === $plan ) { // Plan doesn't exist. $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Plan %s do not exist, therefore, can\'t start a trial.', 'trial-plan-x-not-exist' ), $plan_name ), $oops_text, 'error', $add_sticky_notice ); return false; } if ( ! $plan->has_trial() ) { // Plan doesn't exist. $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Plan %s does not support a trial period.', 'plan-x-no-trial' ), $plan_name ), $oops_text, 'error', $add_sticky_notice ); return false; } } else { if ( ! $this->has_trial_plan() ) { // None of the plans have a trial. $this->_admin_notices->add( sprintf( $this->get_text_inline( 'None of the %s\'s plans supports a trial period.', 'no-trials' ), $this->_module_type ), $oops_text, 'error', $add_sticky_notice ); return false; } $plans_with_trial = FS_Plan_Manager::instance()->get_trial_plans( $this->_plans ); $plan = $plans_with_trial[0]; } $trial_params = array(); if ( $this->is_payments_sandbox() ) { $trial_params['trial_timestamp'] = time(); $trial_params['trial_token'] = FS_Security::instance()->get_trial_token( $this->get_plugin(), $plan, $trial_params['trial_timestamp'] ); } $api = $this->get_api_site_scope(); $trial = $api->call( "plans/{$plan->id}/trials.json", 'post', $trial_params ); if ( ! $this->is_api_result_entity( $trial ) ) { // Some API error while trying to start the trial. $this->_admin_notices->add( $this->get_api_error_message( $trial ), $oops_text, 'error', $add_sticky_notice ); return false; } // Sync license. $this->_sync_license(); return $this->is_trial(); } /** * Cancel site trial. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return object * * @uses FS_Api */ private function _cancel_trial() { $this->_logger->entrance(); if ( ! $this->is_trial() ) { return (object) array( 'error' => (object) array( 'message' => $this->get_text_inline( 'It looks like you are not in trial mode anymore so there\'s nothing to cancel :)', 'trial-cancel-no-trial-message' ) ) ); } $trial_plan = $this->get_trial_plan(); $api = $this->get_api_site_scope(); $site = $api->call( 'trials.json', 'delete' ); $trial_cancelled = false; if ( $this->is_api_result_entity( $site ) ) { $prev_trial_ends = $this->_site->trial_ends; if ( $this->is_paid_trial() ) { $this->_license->expiration = $site->trial_ends; $this->_license->is_cancelled = true; $this->_update_site_license( $this->_license ); $this->_store_licenses(); // Clear subscription reference. $this->_sync_site_subscription( null ); } // Update site info. $this->_site = new FS_Site( $site ); $trial_cancelled = ( $prev_trial_ends != $site->trial_ends ); } else { // @todo handle different error cases. } if ( ! $trial_cancelled ) { return (object) array( 'error' => (object) array( 'message' => $this->get_text_inline( 'Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.', 'trial-cancel-failure-message' ) ) ); } // Remove previous sticky messages about upgrade or trial (if exist). $this->_admin_notices->remove_sticky( array( 'trial_started', 'trial_promotion', 'plan_upgraded', ) ); // Store site updates. $this->_store_site(); if ( ! $this->is_addon() || ! $this->deactivate_premium_only_addon_without_license( true ) ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Your %s free trial was successfully cancelled.', 'trial-cancel-message' ), $trial_plan->title ) ); } return $site; } /** * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param bool|number $plugin_id * * @return bool */ private function _is_addon_id( $plugin_id ) { return is_numeric( $plugin_id ) && ( $this->get_id() != $plugin_id ); } /** * Check if user eligible to download premium version updates. * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @return bool */ private function _can_download_premium() { return $this->has_any_active_valid_license() || ( $this->is_trial() && ! $this->get_trial_plan()->is_free() ); } /** * * @author Vova Feldman (@svovaf) * @since 1.0.6 * * @param bool|number $addon_id * @param string $type "json" or "zip" * * @return string */ private function _get_latest_version_endpoint( $addon_id = false, $type = 'json' ) { $is_addon = $this->_is_addon_id( $addon_id ); $is_premium = null; if ( ! $is_addon ) { $is_premium = ( $this->is_premium() || $this->_can_download_premium() ); } else if ( $this->is_addon_activated( $addon_id ) ) { $fs_addon = self::get_instance_by_id( $addon_id ); $is_premium = ( $fs_addon->is_premium() || $fs_addon->_can_download_premium() ); } // If add-on, then append add-on ID. $endpoint = ( $is_addon ? "/addons/$addon_id" : '' ) . '/updates/latest.' . $type; // If add-on and not yet activated, try to fetch based on server licensing. if ( is_bool( $is_premium ) ) { $endpoint = add_query_arg( 'is_premium', json_encode( $is_premium ), $endpoint ); } if ( $this->has_secret_key() ) { $endpoint = add_query_arg( 'type', 'all', $endpoint ); } else if ( is_object( $this->_site ) && $this->_site->is_beta() ) { $endpoint = add_query_arg( 'type', 'beta', $endpoint ); } return $endpoint; } /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param bool|number $addon_id * @param bool $flush Since 1.1.7.3 * @param int $expiration Since 1.2.2.7 * @param bool|string $newer_than Since 2.2.1 * @param bool|string $fetch_readme Since 2.2.1 * * @return object|false Plugin latest tag info. */ function _fetch_latest_version( $addon_id = false, $flush = true, $expiration = WP_FS__TIME_24_HOURS_IN_SEC, $newer_than = false, $fetch_readme = true ) { $this->_logger->entrance(); if ( $this->is_unresolved_clone( true ) ) { return false; } $switch_to_blog_id = null; /** * @since 1.1.7.3 Check for plugin updates from Freemius only if opted-in. * @since 1.1.7.4 Also check updates for add-ons. */ if ( ( ! $this->is_registered() || ! FS_Permission_Manager::instance( $this )->is_essentials_tracking_allowed() ) && ! $this->_is_addon_id( $addon_id ) ) { if ( ! is_multisite() ) { return false; } $installs_map = $this->get_blog_install_map(); foreach ( $installs_map as $blog_id => $install ) { if ( ! FS_Permission_Manager::instance( $this )->is_essentials_tracking_allowed( $blog_id ) ) { continue; } /** * @var FS_Site $install */ if ( $install->is_trial() ) { $switch_to_blog_id = $blog_id; break; } if ( FS_Plugin_License::is_valid_id( $install->license_id ) ) { $license = $this->get_license_by_id( $install->license_id ); if ( is_object( $license ) && $license->is_features_enabled() ) { $switch_to_blog_id = $blog_id; break; } } } if ( is_null( $switch_to_blog_id ) ) { return false; } } $current_blog_id = is_numeric( $switch_to_blog_id ) ? get_current_blog_id() : 0; if ( is_numeric( $switch_to_blog_id ) ) { $this->switch_to_blog( $switch_to_blog_id ); } $latest_version_endpoint = $this->_get_latest_version_endpoint( $addon_id, 'json' ); if ( ! empty( $newer_than ) ) { $latest_version_endpoint = add_query_arg( 'newer_than', $newer_than, $latest_version_endpoint ); } if ( true === $fetch_readme ) { $latest_version_endpoint = add_query_arg( 'readme', 'true', $latest_version_endpoint ); // Don't cache the API response when fetching readme information. $expiration = null; } $tag = $this->get_api_site_or_plugin_scope()->get( $latest_version_endpoint, $flush, $expiration ); if ( is_numeric( $switch_to_blog_id ) ) { $this->switch_to_blog( $current_blog_id ); } $latest_version = ( is_object( $tag ) && isset( $tag->version ) ) ? $tag->version : 'couldn\'t get'; $this->_logger->departure( 'Latest version ' . $latest_version ); return ( is_object( $tag ) && isset( $tag->version ) ) ? $tag : false; } #---------------------------------------------------------------------------------- #region Download Plugin #---------------------------------------------------------------------------------- /** * Download latest plugin version, based on plan. * * Not like _download_latest(), this will redirect the page * to secure download url to prevent dual download (from FS to WP server, * and then from WP server to the client / browser). * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param bool|number $plugin_id * * @uses FS_Api * @uses wp_redirect() */ private function download_latest_directly( $plugin_id = false ) { $this->_logger->entrance(); wp_redirect( $this->get_latest_download_api_url( $plugin_id ) ); } /** * Get latest plugin FS API download URL. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param bool|number $plugin_id * * @return string */ private function get_latest_download_api_url( $plugin_id = false ) { $this->_logger->entrance(); $download_api_url = $this->get_api_site_scope()->get_signed_url( $this->_get_latest_version_endpoint( $plugin_id, 'zip' ) ); return str_replace( 'http:', 'https:', $download_api_url ); } /** * Get payment invoice URL. * * @author Vova Feldman (@svovaf) * @since 1.2.0 * * @param bool|number $payment_id * * @return string */ function _get_invoice_api_url( $payment_id = false ) { $this->_logger->entrance(); $url = $this->get_api_user_scope()->get_signed_url( "/payments/{$payment_id}/invoice.pdf" ); if ( ! fs_starts_with( $url, 'https://' ) ) { // Always use HTTPS for invoices. $url = 'https' . substr( $url, 4 ); } return $url; } /** * Get latest plugin download link. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param string $label * @param bool|number $plugin_id * * @return string */ private function get_latest_download_link( $label, $plugin_id = false ) { return sprintf( '%s', $this->_get_latest_download_local_url( $plugin_id ), $label ); } /** * Get latest plugin download local URL. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param bool|number $plugin_id * * @return string */ function _get_latest_download_local_url( $plugin_id = false ) { // Add timestamp to protect from caching. $params = array( 'ts' => WP_FS__SCRIPT_START_TIME ); if ( ! empty( $plugin_id ) ) { $params['plugin_id'] = $plugin_id; } else if ( $this->is_addon() ) { $params['plugin_id'] = $this->get_id(); } $fs = $this->is_addon() ? $this->get_parent_instance() : $this; return $this->apply_filters( 'download_latest_url', $fs->get_account_url( 'download_latest', $params ) ); } #endregion Download Plugin ------------------------------------------------------------------ /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @uses FS_Api * * @param bool $background Hints the method if it's a background updates check. If false, it means that * was initiated by the admin. * @param bool|number $plugin_id * @param bool $flush Since 1.1.7.3 * @param int $expiration Since 1.2.2.7 * @param bool|string $newer_than Since 2.2.1 */ private function check_updates( $background = false, $plugin_id = false, $flush = true, $expiration = FS_Plugin_Updater::UPDATES_CHECK_CACHE_EXPIRATION, $newer_than = false ) { $this->_logger->entrance(); // Check if there's a newer version for download. $new_version = $this->_fetch_newer_version( $plugin_id, $flush, $expiration, $newer_than ); $update = null; if ( is_object( $new_version ) ) { $update = new FS_Plugin_Tag( $new_version ); if ( ! $background ) { $this->_admin_notices->add( sprintf( /* translators: %s: Numeric version number (e.g. '2.1.9' */ $this->get_text_inline( 'Version %s was released.', 'version-x-released' ) . ' ' . $this->get_text_inline( 'Please download %s.', 'please-download-x' ), $update->version, sprintf( '%s', $this->get_account_url( 'download_latest' ), sprintf( /* translators: %s: plan name (e.g. latest "Professional" version) */ $this->get_text_inline( 'the latest %s version here', 'latest-x-version' ), $this->get_plan_title() ) ) ), $this->get_text_inline( 'New', 'new' ) . '!' ); } } else if ( false === $new_version && ! $background ) { $this->_admin_notices->add( $this->get_text_inline( 'Seems like you got the latest release.', 'you-have-latest' ), $this->get_text_inline( 'You are all good!', 'you-are-good' ) ); } $this->_store_update( $update, true, $plugin_id ); } /** * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param bool $flush Since 1.1.7.3 add 24 hour cache by default. * * @return FS_Plugin[] * * @uses FS_Api */ private function sync_addons( $flush = false ) { $this->_logger->entrance(); $api = $this->get_api_site_or_plugin_scope(); $path = $this->add_show_pending( '/addons.json?enriched=true&count=50' ); /** * @since 1.2.1 * * If there's a cached version of the add-ons and not asking * for a flush, just use the currently stored add-ons. */ if ( ! $flush && $api->is_cached( $path ) ) { $addons = self::get_all_addons(); return isset( $addons[ $this->_plugin->id ] ) ? $addons[ $this->_plugin->id ] : array(); } $result = $api->get( $path, $flush ); $addons = array(); if ( $this->is_api_result_object( $result, 'plugins' ) && is_array( $result->plugins ) ) { for ( $i = 0, $len = count( $result->plugins ); $i < $len; $i ++ ) { $addons[ $i ] = new FS_Plugin( $result->plugins[ $i ] ); } $this->_store_addons( $addons, true ); } return $addons; } /** * Handle user email update. * * @author Vova Feldman (@svovaf) * @since 1.0.3 * @uses FS_Api * * @param string $new_email * * @return object */ private function update_email( $new_email ) { $this->_logger->entrance(); $api = $this->get_api_user_scope(); $user = $api->call( "?plugin_id={$this->_plugin->id}&fields=id,email,is_verified", 'put', array( 'email' => $new_email, 'after_email_confirm_url' => $this->_get_admin_page_url( 'account', array( 'fs_action' => 'sync_user' ) ), ) ); if ( ! isset( $user->error ) ) { $this->_user->email = $user->email; $this->_user->is_verified = $user->is_verified; $this->_store_user(); } else { // handle different error cases. } return $user; } #---------------------------------------------------------------------------------- #region API Error Handling #---------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 1.1.1 * * @param mixed $result * * @return bool Is API result contains an error. */ private function is_api_error( $result ) { return FS_Api::is_api_error( $result ); } /** * Checks if given API result is a non-empty and not an error object. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $result * @param string|null $required_property Optional property we want to verify that is set. * * @return bool */ function is_api_result_object( $result, $required_property = null ) { return FS_Api::is_api_result_object( $result, $required_property ); } /** * Checks if given API result is a non-empty entity object with non-empty ID. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $result * * @return bool */ private function is_api_result_entity( $result ) { return FS_Api::is_api_result_entity( $result ); } #endregion /** * Make sure a given argument is an array of a specific type. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param mixed $array * @param string $class * * @return bool */ private function is_array_instanceof( $array, $class ) { return ( is_array( $array ) && ( empty( $array ) || $array[0] instanceof $class ) ); } /** * Start install ownership change. * * @author Vova Feldman (@svovaf) * @since 1.1.1 * @uses FS_Api * * @param string $candidate_email * @param string $transfer_type * * @return bool Is ownership change successfully initiated. */ private function init_change_owner( $candidate_email, $transfer_type ) { $this->_logger->entrance(); $installs_info_by_slug_map = $this->get_parent_and_addons_installs_info(); $install_ids = array(); foreach ( $installs_info_by_slug_map as $slug => $install_info ) { $install = $install_info['install']; if ( $this->_user->id != $install->user_id ) { // Skip add-on installs that are not owned by the parent product's install's owner. continue; } $install_ids[ $slug ] = $install->id; } $api = $this->get_api_site_scope(); $result = $api->call( "/users/{$this->_user->id}.json", 'put', array( 'email' => $candidate_email, 'transfer_type' => $transfer_type, 'install_ids' => implode( ',', array_values( $install_ids ) ), 'after_confirm_url' => $this->_get_admin_page_url( 'account', array( 'fs_action' => 'change_owner' ) ), ) ); return ! $this->is_api_error( $result ); } /** * Handle install ownership change. * * @author Vova Feldman (@svovaf) * @since 1.1.1 * @uses FS_Api * * @return bool Was ownership change successfully complete. */ private function complete_change_owner() { $this->_logger->entrance(); $install_ids = fs_request_get( 'install_ids' ); if ( ! empty( $install_ids ) ) { $install_ids = explode( ',', $install_ids ); foreach ( $install_ids as $key => $install_id ) { if ( ! FS_Site::is_valid_id( $install_id ) ) { unset( $install_ids[ $key ] ); } } } if ( ! is_array( $install_ids ) ) { $install_ids = array(); } $user = new FS_User(); $user->id = fs_request_get( 'user_id' ); $user->public_key = fs_request_get_raw( 'user_public_key' ); $user->secret_key = fs_request_get_raw( 'user_secret_key' ); $prev_user = $this->_user; $this->_user = $user; $result = $this->get_api_user_scope( true )->get( "/installs.json?install_ids=" . implode( ',', $install_ids ) ); $current_blog_sites = self::get_all_sites( $this->get_module_type() ); if ( $this->is_api_result_object( $result, 'installs' ) ) { $site_id_slug_map = array(); foreach ( $current_blog_sites as $slug => $site ) { $site_id_slug_map[ $site->id ] = $slug; } foreach ( $result->installs as $install ) { $site = new FS_Site( $install ); if ( ! isset( $site_id_slug_map[ $install->id ] ) ) { continue; } $current_blog_sites[ $site_id_slug_map[ $install->id ] ] = clone $site; if ( $this->_site->id == $site->id ) { $this->_site = $site; } } } // Validate install's user and given user. if ( $user->id != $this->_site->user_id ) { $this->_user = $prev_user; return false; } $this->set_account_option( 'sites', $current_blog_sites, true ); // Fetch new user information. $user_result = $this->get_api_user_scope( true )->get(); $user = new FS_User( $user_result ); $this->_user = $user; $this->_set_account( $user, $this->_site ); $remove_user = true; $all_modules_sites = FS_DebugManager::get_all_modules_sites(); foreach ( $all_modules_sites as $sites_by_module_type ) { foreach ( $sites_by_module_type as $sites_by_slug ) { foreach ( $sites_by_slug as $site ) { if ( $prev_user->id == $site->user_id ) { $remove_user = false; break; } } if ( ! $remove_user ) { break; } } if ( ! $remove_user ) { break; } } if ( $remove_user ) { $users = self::get_all_users(); if ( isset( $users[ $prev_user->id ] ) ) { unset( $users[ $prev_user->id ] ); } else { // If the prev user wasn't found by the key, iterate over the users collection. foreach ( $users as $key => $user ) { if ( $user->id == $prev_user->id ) { unset( $users[ $key ] ); break; } } } $this->set_account_option( 'users', $users, true ); } return true; } /** * Completes ownership change by license. * * @author Leo Fajardo (@leorw) * @since 2.3.2 * * @param number $user_id * @param array[string]number $install_ids_by_slug_map * */ private function complete_ownership_change_by_license( $user_id, $install_ids_by_slug_map ) { $this->_logger->entrance(); $this->sync_user_by_current_install( $user_id ); $result = $this->get_api_user_scope( true )->get( "/installs.json?install_ids=" . implode( ',', $install_ids_by_slug_map ) ); if ( $this->is_api_result_object( $result, 'installs' ) ) { $sites = self::get_all_sites( $this->get_module_type() ); $install_ids_by_slug_map = array_flip( $install_ids_by_slug_map ); foreach ( $result->installs as $install ) { $site = new FS_Site( $install ); $sites[ $install_ids_by_slug_map[ $site->id ] ] = clone $site; } $this->set_account_option( 'sites', $sites, true ); } } /** * Handle user name update. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * @uses FS_Api * * @return object */ private function update_user_name() { $this->_logger->entrance(); $name = fs_request_get( 'fs_user_name_' . $this->get_unique_affix(), '' ); $api = $this->get_api_user_scope(); $user = $api->call( "?plugin_id={$this->_plugin->id}&fields=id,first,last", 'put', array( 'name' => $name, ) ); if ( ! isset( $user->error ) ) { $this->_user->first = $user->first; $this->_user->last = $user->last; $this->_store_user(); } else { // handle different error cases. } return $user; } /** * Verify user email. * * @author Vova Feldman (@svovaf) * @since 1.0.3 * @uses FS_Api */ private function verify_email() { $this->_handle_account_user_sync(); if ( $this->_user->is_verified() ) { return; } $api = $this->get_api_site_scope(); $result = $api->call( "/users/{$this->_user->id}/verify.json", 'put', array( 'after_email_confirm_url' => $this->_get_admin_page_url( 'account', array( 'fs_action' => 'sync_user' ) ) ) ); if ( ! isset( $result->error ) ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Verification mail was just sent to %s. If you can\'t find it after 5 min, please check your spam box.', 'verification-email-sent-message' ), sprintf( '%2$s', esc_url( $this->_user->email ), $this->_user->email ) ) ); } else { // handle different error cases. } } /** * @author Vova Feldman (@svovaf) * @since 1.1.2 * * @param array $params * @param bool|null $network * * @return string */ function get_activation_url( $params = array(), $network = null ) { if ( $this->is_addon() && $this->has_free_plan() ) { /** * @author Vova Feldman (@svovaf) * @since 1.2.1.7 Add-on's activation is the parent's module activation. */ return $this->get_parent_instance()->get_activation_url( $params ); } return $this->apply_filters( 'connect_url', $this->_get_admin_page_url( '', $params, $network ) ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.5 * * @param array $params * * @return string */ function get_reconnect_url( $params = array() ) { $params['fs_action'] = 'reset_anonymous_mode'; $params['fs_unique_affix'] = $this->get_unique_affix(); return $this->get_activation_url( $params ); } /** * Get the URL of the page that should be loaded after the user connect * or skip in the opt-in screen. * * @author Vova Feldman (@svovaf) * @since 1.1.3 * * @param string $filter Filter name. * @param array $params Since 1.2.2.7 * @param bool|null $network * * @return string */ function get_after_activation_url( $filter, $params = array(), $network = null ) { if ( $this->show_opt_in_on_themes_page() && ( fs_request_has( 'pending_activation' ) || // For cases when the first time path is set, even though it's a WP.org theme. fs_request_get_bool( $this->get_unique_affix() . '_show_optin' ) ) ) { $first_time_path = ''; } else { $first_time_path = $this->_menu->get_first_time_path( fs_is_network_admin() && $this->_is_network_active ); } if ( $this->_is_network_active && fs_is_network_admin() && ! $this->_menu->has_network_menu() && $this->is_network_registered() ) { $target_url = $this->get_account_url(); } else { // Default plugin's page. $target_url = $this->_get_admin_page_url( '', array(), $network ); } return add_query_arg( $params, $this->apply_filters( $filter, empty( $first_time_path ) ? $target_url : $first_time_path ) ); } /** * Handle account page updates / edits / actions. * * @author Vova Feldman (@svovaf) * @since 1.0.2 * */ private function _handle_account_edits() { if ( ! $this->is_user_admin() ) { return; } $action = fs_get_action(); if ( empty( $action ) ) { return; } $plugin_id = fs_request_get( 'plugin_id', $this->get_id() ); $install_id = fs_request_get( 'install_id', '' ); // Alias. $oops_text = $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...'; $is_network_action = $this->is_network_level_action(); $blog_id = $this->is_network_level_site_specific_action(); $is_parent_plugin_action = ( $plugin_id == $this->get_id() ); if ( is_numeric( $blog_id ) ) { $this->switch_to_blog( $blog_id ); } else { $blog_id = ''; } switch ( $action ) { case 'opt_in': check_admin_referer( trim( "{$action}:{$blog_id}:{$install_id}", ':' ) ); if ( $is_parent_plugin_action ) { if ( $is_network_action && ! empty( $blog_id ) ) { if ( ! $this->is_registered() ) { $this->install_with_user( $this->get_network_user(), false, false, false, false ); $this->_admin_notices->add( $this->get_text_inline( 'Site successfully opted in.', 'successful-opt-in' ), $this->get_text_inline( 'Awesome', 'awesome' ) ); } } } break; case 'toggle_tracking': check_admin_referer( trim( "{$action}:{$blog_id}:{$install_id}", ':' ) ); if ( $is_parent_plugin_action ) { if ( $is_network_action && ! empty( $blog_id ) ) { if ( $this->is_registered( true ) ) { if ( $this->is_tracking_prohibited( $blog_id ) ) { if ( $this->toggle_site_tracking( true, $blog_id ) ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Sharing diagnostic data with %s helps to provide functionality that\'s more relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the plugin should be translated and tailored to.', 'opt-out-message-appreciation' ), "{$this->get_plugin_title()}" ), $this->get_text_inline( 'Thank you!', 'thank-you' ) ); } } else { if ( $this->toggle_site_tracking( false, $blog_id ) ) { $install = $this->get_install_by_blog_id( $blog_id ); $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Diagnostic data will no longer be sent from %s to %s.', 'opted-out-successfully' ), self::get_unfiltered_site_url( $blog_id, true ), "{$this->get_plugin_title()}" ) ); } } } } } break; case 'delete_account': check_admin_referer( trim( "{$action}:{$blog_id}:{$install_id}", ':' ) ); $is_network_deletion = $is_network_action && empty( $blog_id ); if ( $is_parent_plugin_action ) { // Delete add-on installs if have any. $installed_addons = $this->get_installed_addons(); foreach ( $installed_addons as $fs_addon ) { if ( $is_network_deletion ) { $fs_addon->delete_network_account_event(); } else { $fs_addon->delete_account_event(); } } if ( $is_network_deletion ) { $this->delete_network_account_event(); } else { $this->delete_account_event(); } // Clear user and site. $this->_site = null; $this->_user = null; $this->maybe_set_slug_and_network_menu_exists_flag(); fs_redirect( $this->get_activation_url() ); } else { if ( $this->is_addon_activated( $plugin_id ) ) { $fs_addon = self::get_instance_by_id( $plugin_id ); if ( $is_network_deletion ) { $fs_addon->delete_network_account_event(); } else { $fs_addon->delete_account_event(); } fs_redirect( $this->_get_admin_page_url( 'account' ) ); } } return; case 'downgrade_account': if ( is_numeric( $blog_id ) ) { check_admin_referer( trim( "{$action}:{$blog_id}:{$install_id}", ':' ) ); } else { check_admin_referer( $action ); } $switch_to_network_install_blog_after_cancellation = ( is_numeric( $blog_id ) && $plugin_id == $this->get_id() && ! $this->is_trial() ); $result = $this->cancel_subscription_or_trial( $plugin_id ); if ( $this->is_api_error( $result ) ) { $this->_admin_notices->add( $result->error->message, $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 'error' ); } if ( $switch_to_network_install_blog_after_cancellation ) { $this->switch_to_blog( $this->_storage->network_install_blog_id ); } return; case 'activate_license': check_admin_referer( trim( "{$action}:{$blog_id}:{$install_id}", ':' ) ); $fs = $this; if ( $plugin_id != $this->get_id() ) { $fs = $this->is_addon_activated( $plugin_id ) ? self::get_instance_by_id( $plugin_id ) : null; } if ( is_object( $fs ) ) { $fs->_activate_license(); /** * Remove the product ID from `$_REQUEST` so that the syncing of the license for the other products will work properly. * * @author Leo Fajardo (@leorw) * @since 2.4.0 */ unset( $_REQUEST['plugin_id'] ); if ( $this->is_bundle_license_auto_activation_enabled() ) { $fs->maybe_activate_bundle_license( null, array(), is_numeric( $blog_id ) ? $blog_id : 0 ); } } return; case 'deactivate_license': check_admin_referer( trim( "{$action}:{$blog_id}:{$install_id}", ':' ) ); if ( $plugin_id == $this->get_id() ) { $this->_deactivate_license(); if ( $this->is_only_premium() ) { // Clear user and site. $this->_site = null; $this->_user = null; if ( ! $is_network_action ) { fs_redirect( $this->get_activation_url() ); } else if ( is_numeric( $blog_id ) ) { $this->switch_to_blog( $this->_storage->network_install_blog_id ); } } } else { if ( $this->is_addon_activated( $plugin_id ) ) { $fs_addon = self::get_instance_by_id( $plugin_id ); $fs_addon->_deactivate_license(); } } return; case 'check_updates': check_admin_referer( $action ); $this->check_updates(); return; case 'change_owner': $state = fs_request_get( 'state', 'init' ); switch ( $state ) { case 'init': // The nonce is injected by the error handler in `_email_address_update_ajax_handler` function. check_admin_referer( 'change_owner' ); $candidate_email = fs_request_get( 'candidate_email' ); $transfer_type = fs_request_get( 'transfer_type' ); if ( $this->init_change_owner( $candidate_email, $transfer_type ) ) { if ( 'transfer' === $transfer_type ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours.', 'change-owner-request-sent-x-transfer' ), '' . $this->_user->email . '' ) ); } else { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder.', 'change-owner-request-sent-x' ), '' . $this->_user->email . '' ) ); } } break; case 'owner_confirmed': // We cannot (or need not to) check the nonce and referer here, because the link comes from the email sent by our API. $candidate_email = fs_request_get( 'candidate_email', '' ); if ( ! is_email($candidate_email ) ) { return; } $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Thanks for confirming the ownership change. An email was just sent to %s for final approval.', 'change-owner-request_owner-confirmed' ), '' . $candidate_email . '' ) ); break; case 'candidate_confirmed': // We do not need to validate the authenticity of this request here, because the `complete_change_owner` does that for us through API calls. if ( $this->complete_change_owner() ) { $this->_admin_notices->add_sticky( sprintf( $this->get_text_inline( '%s is the new owner of the account.', 'change-owner-request_candidate-confirmed' ), '' . $this->_user->email . '' ), 'ownership_changed', $this->get_text_x_inline( 'Congrats', 'as congratulations', 'congrats' ) . '!' ); } else { // @todo Handle failed ownership change message. } break; } return; case 'update_user_name': check_admin_referer( 'update_user_name' ); $result = $this->update_user_name(); if ( isset( $result->error ) ) { $this->_admin_notices->add( $this->get_text_inline( 'Please provide your full name.', 'name-update-failed-message' ), $oops_text, 'error' ); } else { $this->_admin_notices->add( $this->get_text_inline( 'Your name was successfully updated.', 'name-updated-message' ) ); } return; #region Actions that might be called from external links (e.g. email) /** * !!IMPORTANT!!: We cannot check for a valid nonce in this region, because the links could be coming from emails. */ case 'cancel_trial': $result = $this->cancel_subscription_or_trial( $plugin_id ); if ( $this->is_api_error( $result ) ) { $this->_admin_notices->add( $result->error->message, $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 'error' ); } return; case 'verify_email': $this->verify_email(); return; case 'sync_user': $this->_handle_account_user_sync(); return; case $this->get_unique_affix() . '_sync_license': $this->_sync_license(); return; case 'download_latest': $this->download_latest_directly( $plugin_id ); return; #endregion } if ( WP_FS__IS_POST_REQUEST ) { $properties = array( 'site_secret_key', 'site_id', 'site_public_key' ); foreach ( $properties as $p ) { if ( 'update_' . $p === $action ) { check_admin_referer( $action ); $this->_logger->log( $action ); $site_property = substr( $p, strlen( 'site_' ) ); $site_property_value = fs_request_get( 'fs_' . $p . '_' . $this->get_unique_affix(), '' ); $this->get_site()->{$site_property} = $site_property_value; // Store account after modification. $this->_store_site(); $this->do_action( 'account_property_edit', 'site', $site_property, $site_property_value ); $this->_admin_notices->add( sprintf( /* translators: %s: User's account property (e.g. email address, name) */ $this->get_text_inline( 'You have successfully updated your %s.', 'x-updated' ), '' . str_replace( '_', ' ', $p ) . '' ) ); return; } } } } /** * Adds CSS classes for the body tag in the admin. * * @param string $classes Space-separated string of class names. * * @return string $classes FS Admin body tag class names. */ public function fs_addons_body_class( $classes ) { $classes .= ' plugins-php'; return $classes; } /** * Account page resources load. * * @author Vova Feldman (@svovaf) * @since 1.0.6 */ function _account_page_load() { $this->_logger->entrance(); $this->_logger->info( var_export( $_REQUEST, true ) ); fs_enqueue_local_style( 'fs_account', '/admin/account.css' ); if ( $this->has_addons() ) { wp_enqueue_script( 'plugin-install' ); add_thickbox(); add_filter( 'admin_body_class', array( $this, 'fs_addons_body_class' ) ); } if ( $this->has_paid_plan() && ! $this->has_any_license() && ! $this->is_sync_executed() && $this->is_tracking_allowed() ) { /** * If no licenses found and no sync job was executed during the last 24 hours, * just execute the sync job right away (blocking execution). * * @since 1.1.7.3 */ $this->run_manual_sync(); } $this->_handle_account_edits(); if ( is_object( $this->_license ) && $this->_license->user_id == $this->_user->id && ! $this->is_whitelabeled( true ) ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( "Is this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin.", 'license_not_whitelabeled' ), sprintf( '%s', $this->get_text_inline( 'Click here', 'click-here' ) ) ), '', 'success', false, 'license_not_whitelabeled' ); } $this->do_action( 'account_page_load_before_departure' ); } /** * Renders the "Affiliation" page. * * @author Leo Fajardo (@leorw) * @since 1.2.3 */ function _affiliation_page_render() { $this->_logger->entrance(); $this->fetch_affiliate_and_terms(); fs_enqueue_local_style( 'fs_affiliation', '/admin/affiliation.css' ); $is_bundle_context = $this->has_bundle_context(); $plugin_title = $this->get_plugin_title(); if ( $is_bundle_context ) { $plugin_title = $this->plugin_affiliate_terms->plugin_title; // Add the suffix "Bundle" only if the word is not present in the title itself. if ( false === mb_stripos( $plugin_title, fs_text_inline( 'Bundle', 'bundle' ) ) ) { $plugin_title = $this->apply_filters( 'formatted_bundle_title', $plugin_title . ' ' . fs_text_inline( 'Bundle', 'bundle' ) ); } } $vars = array( 'id' => $this->_module_id, 'plugin_title' => $plugin_title, ); echo $this->apply_filters( "/forms/affiliation.php", fs_get_template( '/forms/affiliation.php', $vars ) ); } /** * Render account page. * * @author Vova Feldman (@svovaf) * @since 1.0.0 */ function _account_page_render() { $this->_logger->entrance(); $template = 'account.php'; $vars = array( 'id' => $this->_module_id ); /** * Added filter to the template to allow developers wrapping the template * in custom HTML (e.g. within a wizard/tabs). * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 */ echo $this->apply_filters( "templates/{$template}", fs_get_template( $template, $vars ) ); } /** * Render account connect page. * * @author Vova Feldman (@svovaf) * @since 1.0.7 */ function _connect_page_render() { $this->_logger->entrance(); $vars = array( 'id' => $this->_module_id ); /** * Added filter to the template to allow developers wrapping the template * in custom HTML (e.g. within a wizard/tabs). * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 */ echo $this->apply_filters( 'templates/connect.php', fs_get_template( 'connect.php', $vars ) ); } /** * Load required resources before add-ons page render. * * @author Vova Feldman (@svovaf) * @since 1.0.6 */ function _addons_page_load() { $this->_logger->entrance(); fs_enqueue_local_style( 'fs_addons', '/admin/add-ons.css' ); wp_enqueue_script( 'plugin-install' ); add_thickbox(); add_filter( 'admin_body_class', array( $this, 'fs_addons_body_class' ) ); if ( ! $this->is_registered() && $this->is_org_repo_compliant() ) { $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Just letting you know that the add-ons information of %s is being pulled from an external server.', 'addons-info-external-message' ), '' . $this->get_plugin_name() . '' ), $this->get_text_x_inline( 'Heads up', 'advance notice of something that will need attention.', 'heads-up' ), 'update-nag' ); } } /** * Render add-ons page. * * @author Vova Feldman (@svovaf) * @since 1.0.6 */ function _addons_page_render() { $this->_logger->entrance(); $vars = array( 'id' => $this->_module_id ); /** * Added filter to the template to allow developers wrapping the template * in custom HTML (e.g. within a wizard/tabs). * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 */ echo $this->apply_filters( 'templates/add-ons.php', fs_get_template( 'add-ons.php', $vars ) ); } /* Pricing & Upgrade ------------------------------------------------------------------------------------------------------------------*/ /** * Render pricing page. * * @author Vova Feldman (@svovaf) * @since 1.0.0 */ function _pricing_page_render() { $this->_logger->entrance(); fs_enqueue_local_style( 'fs_common', '/admin/common.css' ); fs_enqueue_local_style( 'fs_checkout', '/admin/checkout.css' ); $vars = array( 'id' => $this->_module_id ); if ( 'true' === fs_request_get( 'checkout', false ) ) { echo $this->apply_filters( 'templates/checkout.php', fs_get_template( 'checkout.php', $vars ) ); } else { echo $this->apply_filters( 'templates/pricing.php', fs_get_template( 'pricing.php', $vars ) ); } } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 */ function _add_pricing_ajax_handler() { $this->add_ajax_action( 'pricing_ajax_action', array( &$this, '_fs_pricing_ajax_action_handler' ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.1 */ function _fs_pricing_ajax_action_handler() { $this->check_ajax_referer( 'pricing_ajax_action' ); $result = null; $pricing_action = fs_request_get( 'pricing_action' ); switch ( $pricing_action ) { case 'fetch_pricing_data': $params = array( 'is_enriched' => true, 'trial' => fs_request_get_bool( 'trial' ), 'sandbox' => fs_request_get_raw( 'sandbox' ), 's_ctx_type' => fs_request_get_raw( 's_ctx_type' ), 's_ctx_id' => fs_request_get_raw( 's_ctx_id' ), 's_ctx_ts' => fs_request_get_raw( 's_ctx_ts' ), 's_ctx_secure' => fs_request_get_raw( 's_ctx_secure' ), ); $bundle_id = $this->get_bundle_id(); $bundle_public_key = $this->get_bundle_public_key(); $has_bundle_context = ( FS_Plugin::is_valid_id( $bundle_id ) && ! empty( $bundle_public_key ) ); if ( ! $has_bundle_context ) { $api = $this->get_api_plugin_scope(); } else { $api = FS_Api::instance( $bundle_id, 'plugin', $bundle_id, $bundle_public_key, ! $this->is_live(), false, $this->get_sdk_version() ); $params['plugin_id'] = $this->get_id(); $params['plugin_public_key'] = $this->get_public_key(); } $result = $api->get( 'pricing.json?' . http_build_query( $params ) ); break; case 'start_trial': $trial_plan_id = fs_request_get( 'plan_id' ); if ( $this->is_registered() && $this->is_tracking_allowed() ) { $plan = $this->_get_plan_by_id( $trial_plan_id ); if ( ! $plan ) { $this->shoot_ajax_failure( 'Invalid plan ID.' ); return; } $result = $this->start_trial( $plan->name, true ); } else { // @todo - This fails for sandbox trial at the moment if the trial was already utilized. $result = $this->opt_in( false, false, false, false, false, $trial_plan_id ); } } if ( is_object( $result ) && $this->is_api_error( $result ) ) { $this->_logger->api_error( $result ); self::shoot_ajax_failure( isset( $result->error ) ? ( is_string( $result->error ) ? $result->error : $result->error->message ) : var_export( $result, true ) ); } $this->shoot_ajax_success( $result ); } #---------------------------------------------------------------------------------- #region Contact Us #---------------------------------------------------------------------------------- /** * Render contact-us page. * * @author Vova Feldman (@svovaf) * @since 1.0.3 */ function _contact_page_render() { $this->_logger->entrance(); $vars = array( 'id' => $this->_module_id ); /** * Added filter to the template to allow developers wrapping the template * in custom HTML (e.g. within a wizard/tabs). * * @author Vova Feldman (@svovaf) * @since 2.1.3 */ echo $this->apply_filters( 'templates/contact.php', fs_get_template( 'contact.php', $vars ) ); } #endregion ------------------------------------------------------------------------ /** * Hide all admin notices to prevent distractions. * * @author Vova Feldman (@svovaf) * @since 1.0.3 * * @uses remove_all_actions() */ private static function _hide_admin_notices() { remove_all_actions( 'admin_notices' ); remove_all_actions( 'network_admin_notices' ); remove_all_actions( 'all_admin_notices' ); remove_all_actions( 'user_admin_notices' ); } static function _clean_admin_content_section_hook() { $hide_admin_notices = true; if ( fs_request_is_action( 'allow_clone_resolution_notice' ) ) { check_admin_referer( 'fs_allow_clone_resolution_notice' ); $hide_admin_notices = false; } if ( $hide_admin_notices ) { self::_hide_admin_notices(); } // Hide footer. echo ''; } /** * Attach to admin_head hook to hide all admin notices. * * @author Vova Feldman (@svovaf) * @since 1.0.3 */ static function _clean_admin_content_section() { add_action( 'admin_head', 'Freemius::_clean_admin_content_section_hook' ); } /* CSS & JavaScript ------------------------------------------------------------------------------------------------------------------*/ /* function _enqueue_script($handle, $src) { $url = plugins_url( substr( WP_FS__DIR_JS, strlen( $this->_plugin_dir_path ) ) . '/assets/js/' . $src ); $this->_logger->entrance( 'script = ' . $url ); wp_enqueue_script( $handle, $url ); }*/ /* SDK ------------------------------------------------------------------------------------------------------------------*/ private $_user_api; /** * * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @param bool $flush * * @return FS_Api */ function get_api_user_scope( $flush = false ) { if ( ! isset( $this->_user_api ) || $flush ) { $this->_user_api = $this->get_api_user_scope_by_user( $this->_user ); } return $this->_user_api; } /** * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @param \FS_User $user * * @return \FS_Api */ private function get_api_user_scope_by_user( FS_User $user ) { return FS_Api::instance( $this->_module_id, 'user', $user->id, $user->public_key, ! $this->is_live(), $user->secret_key, $this->get_sdk_version() ); } /** * * @author Leo Fajardo (@leorw) * @since 2.0.0 * * @param bool $flush * * @return FS_Api */ private function get_current_or_network_user_api_scope( $flush = false ) { if ( ! $this->_is_network_active || ( isset( $this->_user ) && $this->_user instanceof FS_User ) ) { return $this->get_api_user_scope( $flush ); } $user = $this->get_current_or_network_user(); $this->_user_api = FS_Api::instance( $this->_module_id, 'user', $user->id, $user->public_key, ! $this->is_live(), $user->secret_key, $this->get_sdk_version() ); return $this->_user_api; } private $_site_api; /** * * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @param bool $flush * * @return FS_Api */ private function get_api_site_scope( $flush = false ) { if ( ! isset( $this->_site_api ) || $flush ) { $this->_site_api = FS_Api::instance( $this->_module_id, 'install', $this->_site->id, $this->_site->public_key, ! $this->is_live(), $this->_site->secret_key, $this->get_sdk_version(), self::get_unfiltered_site_url() ); } return $this->_site_api; } /** * @author Leo Fajardo (@leorw) * @since 2.5.0 * * @param string $path * @param string $method * @param array $params * @param bool $flush_instance * * @return array|mixed|string|void * @throws Freemius_Exception */ private function api_site_call( $path, $method = 'GET', $params = array(), $flush_instance = false ) { $result = $this->get_api_site_scope( $flush_instance )->call( $path, $method, $params ); /** * Checks if the local install's URL is different from the remote install's URL, update the local install if necessary, and then run the clone handler if the install's URL is different from the URL of the site. * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ if ( $this->is_registered() && FS_Api::is_api_result_entity( $result ) && isset( $result->url ) ) { $stored_local_url = trailingslashit( $this->_site->url ); $stored_remote_url = trailingslashit( $result->url ); if ( $stored_local_url !== $stored_remote_url ) { $this->_site->url = $result->url; $this->_store_site(); } if ( fs_strip_url_protocol( $stored_remote_url ) !== self::get_unfiltered_site_url( null, true, true ) ) { FS_Clone_Manager::instance()->maybe_run_clone_resolution(); } } return $result; } private $_plugin_api; /** * Get plugin public API scope. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return FS_Api */ function get_api_plugin_scope() { if ( ! isset( $this->_plugin_api ) ) { $this->_plugin_api = FS_Api::instance( $this->_module_id, 'plugin', $this->_plugin->id, $this->_plugin->public_key, ! $this->is_live(), false, $this->get_sdk_version() ); } return $this->_plugin_api; } /** * Get bundle public API scope. * * @author Vova Feldman (@svovaf) * @since 2.3.1 * * @return FS_Api */ function get_api_bundle_scope() { return FS_Api::instance( $this->get_bundle_id(), 'plugin', $this->get_bundle_id(), $this->get_bundle_public_key(), ! $this->is_live(), false, $this->get_sdk_version() ); } /** * Get site API scope object (fallback to public plugin scope when not registered). * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return FS_Api */ function get_api_site_or_plugin_scope() { return $this->is_registered() ? $this->get_api_site_scope() : $this->get_api_plugin_scope(); } /** * @author Leo Fajardo (@leorw) * @since 2.2.3.1 * * @param object $result */ private function maybe_modify_api_curl_error_message( $result ) { if ( 'cUrlMissing' !== $result->error->type && ( 'CurlException' !== $result->error->type || CURLE_COULDNT_CONNECT != $result->error->code ) && ( 'HttpRequestFailed' !== $result->error->type || false === strpos( $result->error->message, 'cURL error ' . CURLE_COULDNT_CONNECT ) ) ) { return; } $result->error->message = $this->esc_html_inline( 'We use PHP cURL library for the API calls, which is a very common library and usually installed and activated out of the box. Unfortunately, cURL is not activated (or disabled) on your server.', 'curl-missing-message' ) . ' ' . $this->esc_html_inline( sprintf( 'Please contact your hosting provider and ask them to whitelist %s for external connection.', implode( ', ', $this->apply_filters( 'api_domains', array( 'api.freemius.com', 'wp.freemius.com' ) ) ) ), 'connectivity-whitelist' ) . ' ' . sprintf( $this->esc_html_inline( 'Once you are done, deactivate the %s and activate it again.', 'connectivity-reactivate-module' ), $this->get_module_type() ); } /** * Show trial promotional notice (if any trial exist). * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @param FS_Plugin_Plan[] $plans */ function _check_for_trial_plans( $plans ) { /** * For some reason core's do_action() flattens arrays when it has a single object item. Therefore, we need to restructure the array as expected. * * @author Vova Feldman (@svovaf) * @since 2.1.2 */ if ( ! is_array( $plans ) && is_object( $plans ) ) { $plans = array( $plans ); } if ( ! $this->is_array_instanceof( $plans, 'FS_Plugin_Plan' ) ) { $plans = array(); } $this->_storage->has_trial_plan = FS_Plan_Manager::instance()->has_trial_plan( $plans ); } /** * During trial promotion the "upgrade" submenu item turns to * "start trial" to encourage the trial. Since we want to keep * the same menu item handler and there's no robust way to * add new arguments to the menu item link's querystring, * use JavaScript to find the menu item and update the href of * the link. * * @author Vova Feldman (@svovaf) * @since 1.2.1.5 */ function _fix_start_trial_menu_item_url() { $template_args = array( 'id' => $this->_module_id ); fs_require_template( 'add-trial-to-pricing.php', $template_args ); } /** * Check if module is currently in a trial promotion mode. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool */ function is_in_trial_promotion() { return $this->_admin_notices->has_sticky( 'trial_promotion' ); } /** * Show trial promotional notice (if any trial exist). * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool If trial notice added. */ function _add_trial_notice() { if ( ! $this->is_user_admin() ) { return false; } if ( ! $this->is_user_in_admin() ) { return false; } if ( $this->_is_network_active ) { if ( fs_is_network_admin() ) { // Network level trial is disabled at the moment. return false; } if ( ! $this->is_delegated_connection() ) { // Only delegated sites should support trials. return false; } } // Check if trial message is already shown. if ( $this->is_in_trial_promotion() ) { add_action( 'admin_footer', array( &$this, '_fix_start_trial_menu_item_url' ) ); $this->_menu->add_counter_to_menu_item( 1, 'fs-trial' ); return false; } if ( $this->is_premium() && ! WP_FS__DEV_MODE ) { // Don't show trial if running the premium code, unless running in DEV mode. return false; } if ( ! $this->has_trial_plan() ) { // No plans with trial. return false; } if ( ! $this->apply_filters( 'show_trial', true ) ) { // Developer explicitly asked not to show the trial promo. return false; } if ( $this->is_registered() ) { // Check if trial already utilized. if ( $this->_site->is_trial_utilized() ) { return false; } if ( $this->is_paying_or_trial() ) { // Don't show trial if paying or already in trial. return false; } } if ( $this->is_activation_mode() || $this->is_pending_activation() ) { // If not yet opted-in/skipped, or pending activation, don't show trial. return false; } $last_time_trial_promotion_shown = $this->_storage->get( 'trial_promotion_shown', false ); $was_promotion_shown_before = ( false !== $last_time_trial_promotion_shown ); // Show promotion if never shown before and 24 hours after initial activation with FS. if ( ! $was_promotion_shown_before && $this->_storage->install_timestamp > ( time() - $this->apply_filters( 'show_first_trial_after_n_sec', WP_FS__TIME_24_HOURS_IN_SEC ) ) ) { return false; } // OR if promotion was shown before, try showing it every 30 days. if ( $was_promotion_shown_before && $this->apply_filters( 'reshow_trial_after_every_n_sec', 30 * WP_FS__TIME_24_HOURS_IN_SEC ) > time() - $last_time_trial_promotion_shown ) { return false; } $trial_period = $this->_trial_days; $require_payment = $this->_is_trial_require_payment; $trial_url = $this->get_trial_url(); $plans_string = strtolower( $this->get_text_inline( 'Awesome', 'awesome' ) ); if ( $this->is_registered() ) { // If opted-in, override trial with up to date data from API. $trial_plans = FS_Plan_Manager::instance()->get_visible_trial_plans( $this->_plans ); $trial_plans_count = count( $trial_plans ); if ( 0 === $trial_plans_count ) { // If there's no plans with a trial just exit. return false; } /** * @var FS_Plugin_Plan $paid_plan */ $paid_plan = $trial_plans[0]; $require_payment = $paid_plan->is_require_subscription; $trial_period = $paid_plan->trial_period; $total_paid_plans = count( $this->_plans ) - ( FS_Plan_Manager::instance()->has_free_plan( $this->_plans ) ? 1 : 0 ); if ( $total_paid_plans !== $trial_plans_count ) { // Not all paid plans have a trial - generate a string of those that have it. for ( $i = 0; $i < $trial_plans_count; $i ++ ) { $plans_string .= sprintf( ' %s', $trial_url, $trial_plans[ $i ]->title ); if ( $i < $trial_plans_count - 2 ) { $plans_string .= ', '; } else if ( $i == $trial_plans_count - 2 ) { $plans_string .= ' and '; } } } } $message = sprintf( $this->get_text_x_inline( 'Hey', 'exclamation', 'hey' ) . '! ' . $this->get_text_inline( 'How do you like %s so far? Test all our %s premium features with a %d-day free trial.', 'trial-x-promotion-message' ), sprintf( '%s', $this->get_plugin_name() ), $plans_string, $trial_period ); // "No Credit-Card Required" or "No Commitment for N Days". $cc_string = $require_payment ? sprintf( $this->get_text_inline( 'No commitment for %s days - cancel anytime!', 'no-commitment-for-x-days' ), $trial_period ) : $this->get_text_inline( 'No credit card required', 'no-cc-required' ) . '!'; // Start trial button. $button = ' ' . sprintf( '', $trial_url, $this->get_text_x_inline( 'Start free trial', 'call to action', 'start-free-trial' ) ); $this->_admin_notices->add_sticky( $this->apply_filters( 'trial_promotion_message', "{$message} {$cc_string} {$button}" ), 'trial_promotion', '', 'promotion' ); $this->_storage->trial_promotion_shown = WP_FS__SCRIPT_START_TIME; return true; } /** * Lets users/customers know that the product has an affiliate program. * * @author Leo Fajardo (@leorw) * @since 1.2.2.11 * * @return bool Returns true if the notice has been added. */ function _add_affiliate_program_notice() { if ( ! $this->is_user_admin() ) { return false; } if ( ! $this->is_user_in_admin() ) { return false; } // Check if the notice is already shown. if ( $this->_admin_notices->has_sticky( 'affiliate_program' ) ) { return false; } if ( // Product has no affiliate program. ! $this->has_affiliate_program() || // User has applied for an affiliate account. ! empty( $this->_storage->affiliate_application_data ) ) { return false; } if ( ! $this->apply_filters( 'show_affiliate_program_notice', true ) ) { // Developer explicitly asked not to show the notice about the affiliate program. return false; } if ( $this->is_activation_mode() || $this->is_pending_activation() ) { // If not yet opted in/skipped, or pending activation, don't show the notice. return false; } $last_time_notice_was_shown = $this->_storage->get( 'affiliate_program_notice_shown', false ); $was_notice_shown_before = ( false !== $last_time_notice_was_shown ); /** * Do not show the notice if it was already shown before or less than 30 days have passed since the initial * activation with FS. */ if ( $was_notice_shown_before || $this->_storage->install_timestamp > ( time() - ( WP_FS__TIME_24_HOURS_IN_SEC * 30 ) ) ) { return false; } if ( ! $this->is_paying() && FS_Plugin::AFFILIATE_MODERATION_CUSTOMERS == $this->_plugin->affiliate_moderation ) { // If the user is not a customer and the affiliate program is only for customers, don't show the notice. return false; } $message = sprintf( $this->get_text_inline( 'Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!', 'become-an-ambassador-admin-notice' ), sprintf( '%s', $this->get_plugin_name() ), $this->get_module_label( true ) ); // HTML code for the "Learn more..." button. $button = ' ' . sprintf( '', $this->_get_admin_page_url( 'affiliation' ), $this->get_text_inline( 'Learn more', 'learn-more' ) . '...' ); $this->_admin_notices->add_sticky( $this->apply_filters( 'affiliate_program_notice', "{$message} {$button}" ), 'affiliate_program', '', 'promotion' ); $this->_storage->affiliate_program_notice_shown = WP_FS__SCRIPT_START_TIME; return true; } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.5 */ function _enqueue_common_css() { if ( $this->has_paid_plan() && ! $this->is_paying() ) { // Add basic CSS for admin-notices and menu-item colors. fs_enqueue_local_style( 'fs_common', '/admin/common.css' ); } } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 */ function _show_theme_activation_optin_dialog() { fs_enqueue_local_style( 'fs_connect', '/admin/connect.css' ); add_action( 'admin_footer', array( &$this, '_add_fs_theme_activation_dialog' ) ); } /** * @author Leo Fajardo (@leorw) * @since 1.2.2 */ function _add_fs_theme_activation_dialog() { global $pagenow; if ( 'themes.php' !== $pagenow ) { return; } $vars = array( 'id' => $this->_module_id ); fs_require_once_template( 'connect.php', $vars ); } /* Action Links ------------------------------------------------------------------------------------------------------------------*/ private $_action_links_hooked = false; private $_action_links = array(); /** * Hook to plugin action links filter. * * @author Vova Feldman (@svovaf) * @since 1.0.0 */ private function hook_plugin_action_links() { $this->_logger->entrance(); $this->_action_links_hooked = true; $this->_logger->log( 'Adding action links hooks.' ); // Add action link to settings page. add_filter( 'plugin_action_links_' . $this->_plugin_basename, array( &$this, '_modify_plugin_action_links_hook' ), WP_FS__DEFAULT_PRIORITY, 2 ); add_filter( 'network_admin_plugin_action_links_' . $this->_plugin_basename, array( &$this, '_modify_plugin_action_links_hook' ), WP_FS__DEFAULT_PRIORITY, 2 ); } /** * Add plugin action link. * * @author Vova Feldman (@svovaf) * @since 1.0.0 * * @param $label * @param $url * @param bool $external * @param int $priority * @param bool $key */ function add_plugin_action_link( $label, $url, $external = false, $priority = WP_FS__DEFAULT_PRIORITY, $key = false ) { $this->_logger->entrance(); if ( ! isset( $this->_action_links[ $priority ] ) ) { $this->_action_links[ $priority ] = array(); } if ( false === $key ) { $key = preg_replace( "/[^A-Za-z0-9 ]/", '', strtolower( $label ) ); } $this->_action_links[ $priority ][] = array( 'label' => $label, 'href' => $url, 'key' => $key, 'external' => $external ); } /** * Adds Upgrade and Add-Ons links to the main Plugins page link actions collection. * * @author Vova Feldman (@svovaf) * @since 1.0.0 */ function _add_upgrade_action_link() { $this->_logger->entrance(); $is_activation_mode = $this->is_activation_mode(); $add_action_links = $this->should_add_submenu_or_action_links( $is_activation_mode ); /** * The following logic is based on the logic in `add_submenu_items()` method that decides when the "Upgrade" * and "Add-Ons" menus should be added. * * @author Leo Fajardo (@leorw) * @since 2.3.0 */ $add_upgrade_link = ( $add_action_links || ( $is_activation_mode && $this->is_only_premium() ) ) && ! WP_FS__DEMO_MODE && ( ! $this->is_whitelabeled() ); $add_addons_link = ( $add_action_links && $this->has_addons() ); if ( ! $add_upgrade_link && ! $add_addons_link ) { return; } if ( $add_upgrade_link && $this->is_pricing_page_visible() && $this->is_submenu_item_visible( 'pricing' ) ) { $this->add_plugin_action_link( $this->get_text_inline( 'Upgrade', 'upgrade' ), $this->get_upgrade_url(), false, 7, 'upgrade' ); } if ( $add_addons_link && $this->has_addons() && $this->is_submenu_item_visible( 'addons' ) ) { $this->add_plugin_action_link( $this->get_text_inline( 'Add-Ons', 'add-ons' ), $this->_get_admin_page_url( 'addons' ), false, 9, 'addons' ); } } /** * Adds "Activate License" or "Change License" link to the main Plugins page link actions collection. * * @author Leo Fajardo (@leorw) * @since 1.1.9 */ function _add_license_action_link() { $this->_logger->entrance(); if ( ! self::is_ajax() ) { // Inject license activation dialog UI and client side code. add_action( 'admin_footer', array( &$this, '_add_license_activation_dialog_box' ) ); } $link_text = $this->is_free_plan() ? $this->get_text_inline( 'Activate License', 'activate-license' ) : $this->get_text_inline( 'Change License', 'change-license' ); $this->add_plugin_action_link( $link_text, '#', false, 11, ( 'activate-license ' . $this->get_unique_affix() ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.0.2 */ function _add_premium_version_upgrade_selection_action() { $this->_logger->entrance(); if ( ! self::is_ajax() ) { add_action( 'admin_footer', array( &$this, '_add_premium_version_upgrade_selection_dialog_box' ) ); } } /** * Adds "Opt In" or "Opt Out" link to the main "Plugins" page link actions collection. * * @author Leo Fajardo (@leorw) * @since 1.2.1.5 */ function _add_tracking_links() { if ( ! current_user_can( 'manage_options' ) ) { return; } $this->_logger->entrance(); if ( $this->is_only_premium() && $this->is_free_plan() ) { // Don't add tracking links for premium-only products that were opted-in by relation (add-on or a parent product) before activating any license. return; } if ( $this->is_addon() && ! $this->is_only_premium() ) { $parent = $this->get_parent_instance(); if ( is_object( $parent ) && $parent->is_anonymous() ) { return; } } if ( fs_is_network_admin() ) { if ( ! $this->_is_network_active ) { // Don't add tracking links when browsing the network WP Admin and the plugin is not network active. return; } else if ( $this->is_network_delegated_connection() ) { // Don't add tracking links when browsing the network WP Admin and the activation has been delegated to site admins. return; } } else { if ( $this->_is_network_active && ! $this->is_delegated_connection() ) { // Don't add tracking links when browsing the sub-site WP Admin, the plugin is network active, and the connection was not delegated. return; } } if ( fs_request_is_action_secure( $this->get_unique_affix() . '_reconnect' ) ) { if ( ! $this->is_registered() && $this->is_anonymous() ) { $this->connect_again(); return; } } if ( ( $this->is_plugin() && ! self::is_plugins_page() ) || ( $this->is_theme() && ! self::is_themes_page() ) ) { // Only show tracking links on the plugins and themes pages. return; } if ( $this->is_activation_mode() && $this->is_premium() && ! $this->is_registered() ) { // If not yet registered and running the premium code base, a license activation link will already be shown. return; } if ( $this->is_registered() && $this->is_tracking_allowed() ) { if ( ! $this->is_premium() && ! $this->is_enable_anonymous() ) { // If opted in and tracking is allowed, don't allow to opt out if not premium and anonymous mode is disabled. return; } } if ( $this->add_ajax_action( 'toggle_permission_tracking', array( &$this, '_toggle_permission_tracking_callback' ) ) ) { return; } $link_text_id = ''; $url = '#'; if ( $this->is_registered( true ) ) { if ( $this->is_registered() && $this->is_tracking_allowed() ) { $link_text_id = $this->get_text_inline( 'Opt Out', 'opt-out' ); } else { $link_text_id = $this->get_text_inline( 'Opt In', 'opt-in' ); } } else if ( $this->is_anonymous() || $this->is_activation_mode() ) { /** * Show opt-in link only if skipped or in activation mode. */ $link_text_id = $this->get_text_inline( 'Opt In', 'opt-in' ); $params = ! $this->is_anonymous() ? array() : array( 'nonce' => wp_create_nonce( $this->get_unique_affix() . '_reconnect' ), 'fs_action' => ( $this->get_unique_affix() . '_reconnect' ), ); $url = $this->get_activation_url( $params ); } add_action( 'admin_footer', array( &$this, '_add_optout_dialog' ) ); if ( ! empty( $link_text_id ) && $this->is_plugin() && self::is_plugins_page() ) { $this->add_plugin_action_link( $link_text_id, $url, false, 13, "opt-in-or-opt-out {$this->_slug}" ); } } /** * Get the URL of the page that should be loaded right after the plugin activation. * * @author Vova Feldman (@svovaf) * @since 1.1.7.4 * * @return string */ function get_after_plugin_activation_redirect_url() { $url = false; if ( ! $this->is_addon() || ! $this->has_free_plan() ) { $first_time_path = $this->_menu->get_first_time_path( fs_is_network_admin() && $this->_is_network_active ); if ( $this->is_activation_mode() ) { $url = $this->get_activation_url(); } else if ( ! empty( $first_time_path ) ) { $url = $first_time_path; } else { $page = ''; if ( ! empty( $this->_dynamically_added_top_level_page_hook_name ) ) { if ( $this->is_network_registered() ) { $page = 'account'; } else if ( $this->is_pending_activation() || $this->is_network_anonymous() ) { $this->maybe_set_slug_and_network_menu_exists_flag(); } } $url = $this->_get_admin_page_url( $page ); } } else { $plugin_fs = false; if ( $this->is_parent_plugin_installed() ) { $plugin_fs = self::get_parent_instance(); } if ( is_object( $plugin_fs ) ) { if ( ! $plugin_fs->is_registered() ) { // Forward to parent plugin connect when parent not registered. $url = $plugin_fs->get_activation_url(); } else { // Forward to account page. $url = $plugin_fs->_get_admin_page_url( 'account' ); } } } return $url; } /** * Forward page to activation page. * * @author Vova Feldman (@svovaf) * @since 1.0.3 */ function _redirect_on_activation_hook() { if ( $this->apply_filters( 'redirect_on_activation', true ) ) { $url = $this->get_after_plugin_activation_redirect_url(); if ( is_string( $url ) ) { fs_redirect( $url ); } } } /** * Modify plugin's page action links collection. * * @author Vova Feldman (@svovaf) * @since 1.0.0 * * @param array $links * @param $file * * @return array */ function _modify_plugin_action_links_hook( $links, $file ) { $this->_logger->entrance(); $passed_deactivate = false; $deactivate_link = ''; $before_deactivate = array(); $after_deactivate = array(); foreach ( $links as $key => $link ) { if ( 'deactivate' === $key ) { $deactivate_link = $link; $passed_deactivate = true; continue; } if ( ! $passed_deactivate ) { $before_deactivate[ $key ] = $link; } else { $after_deactivate[ $key ] = $link; } } ksort( $this->_action_links ); foreach ( $this->_action_links as $new_links ) { foreach ( $new_links as $link ) { $before_deactivate[ $link['key'] ] = '' . $link['label'] . ''; } } if ( ! empty( $deactivate_link ) ) { /** * This HTML element is used to identify the correct plugin when attaching an event to its Deactivate link. * * @since 1.2.1.6 Always show the deactivation feedback form since we added automatic free version deactivation upon premium code activation. */ $deactivate_link .= ''; // Append deactivation link. $before_deactivate['deactivate'] = $deactivate_link; } return array_merge( $before_deactivate, $after_deactivate ); } /** * Adds admin message. * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @param string $message * @param string $title * @param string $type */ function add_admin_message( $message, $title = '', $type = 'success' ) { $this->_admin_notices->add( $message, $title, $type ); } /** * Adds sticky admin message. * * @author Vova Feldman (@svovaf) * @since 1.1.0 * * @param string $message * @param string $id * @param string $title * @param string $type */ function add_sticky_admin_message( $message, $id, $title = '', $type = 'success' ) { $this->_admin_notices->add_sticky( $message, $id, $title, $type ); } /** * Check if the paid version of the module is installed. * * @author Vova Feldman (@svovaf) * @since 2.2.0 * * @return bool */ private function is_premium_version_installed() { $premium_plugin_basename = $this->premium_plugin_basename(); if ( $this->is_theme() ) { return $this->can_activate_theme( $this->get_premium_slug() ); } return file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $premium_plugin_basename ) ); } /** * Helper function that returns the final steps for the upgrade completion. * * If the module is already running the premium code, returns an empty string. * * @author Vova Feldman (@svovaf) * @since 1.2.1 * * @param string $plan_title * * @return string */ private function get_complete_upgrade_instructions( $plan_title = '' ) { $this->_logger->entrance(); $activate_license_string = $this->get_license_network_activation_notice(); if ( ! $this->has_premium_version() || $this->is_premium() ) { return '' . $activate_license_string; } if ( empty( $plan_title ) ) { $plan_title = $this->get_plan_title(); } if ( $this->is_premium_version_installed() ) { /** * If the premium version is already installed, instead of showing the installation instructions, * tell the current user to activate it. * * @author Leo Fajardo (@leorw) * @since 2.2.1 */ $premium_theme_slug_or_plugin_basename = $this->is_theme() ? $this->get_premium_slug() : $this->premium_plugin_basename(); return sprintf( /* translators: %1$s: Product title; %2$s: Plan title */ $this->get_text_inline( ' The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s', 'activate-premium-version' ), sprintf( '%s', esc_html( $this->get_plugin_title() ) ), $plan_title, sprintf( '', ( $this->is_theme() ? wp_nonce_url( 'themes.php?action=activate&stylesheet=' . $premium_theme_slug_or_plugin_basename, 'switch-theme_' . $premium_theme_slug_or_plugin_basename ) : wp_nonce_url( 'plugins.php?action=activate&plugin=' . $premium_theme_slug_or_plugin_basename, 'activate-plugin_' . $premium_theme_slug_or_plugin_basename ) ), esc_html( sprintf( /* translators: %s: Plan title */ $this->get_text_inline( 'Activate %s features', 'activate-x-features' ), $plan_title ) ) ) ); } else { // @since 1.2.1.5 The free version is auto deactivated. $deactivation_step = version_compare( $this->version, '1.2.1.5', '<' ) ? ( '
  • ' . $this->esc_html_inline( 'Deactivate the free version', 'deactivate-free-version' ) . '.
  • ' ) : ''; return sprintf( ' %s:
    1. %s.
    2. %s
    3. %s (%s).
    ', $this->get_text_inline( 'Please follow these steps to complete the upgrade', 'follow-steps-to-complete-upgrade' ), ( empty( $activate_license_string ) ? '' : $activate_license_string . '
  • ' ) . $this->get_latest_download_link( sprintf( /* translators: %s: Plan title */ $this->get_text_inline( 'Download the latest %s version', 'download-latest-x-version' ), $plan_title ) ), $deactivation_step, $this->get_text_inline( 'Upload and activate the downloaded version', 'upload-and-activate' ), $this->apply_filters( 'upload_and_install_video_url', '//bit.ly/wp-' . $this->_module_type . '-upload' ), $this->get_text_inline( 'How to upload and activate?', 'howto-upload-activate' ) ); } } /** * @author Leo Fajardo (@leorw) * @since 2.5.3 * * @param string $message_before_the_instructions * @param string $message_id * @param string $plan_title */ private function add_complete_upgrade_instructions_notice( $message_before_the_instructions, $message_id, $plan_title = '' ) { $this->_admin_notices->add_sticky( $message_before_the_instructions . $this->get_complete_upgrade_instructions( $plan_title ), $message_id, $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!' ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.3 * * @param bool $is_upgrade */ private function add_after_plan_activation_or_upgrade_instructions_notice( $is_upgrade = true ) { $this->add_complete_upgrade_instructions_notice( $is_upgrade ? $this->get_text_inline( 'Your plan was successfully upgraded.', 'plan-upgraded-message' ) : $this->get_text_inline( 'Your plan was successfully activated.', 'plan-activated-message' ), 'plan_upgraded' ); } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 * * @param string $url * @param array $request * @param int $success_cache_expiration * @param int $failure_cache_expiration * @param bool $maybe_enrich_request_for_debug * * @return WP_Error|array */ static function safe_remote_post( &$url, $request, $success_cache_expiration = 0, $failure_cache_expiration = 0, $maybe_enrich_request_for_debug = true ) { $should_cache = ($success_cache_expiration + $failure_cache_expiration > 0); $cache_key = $should_cache ? md5( fs_strip_url_protocol($url) . json_encode( $request ) ) : false; $response = (!WP_FS__DEBUG_SDK && ( false !== $cache_key )) ? get_transient( $cache_key ) : false; if ( false === $response ) { if ( $maybe_enrich_request_for_debug ) { FS_DebugManager::enrich_request_for_debug( $url, $request ); } if ( ! isset( $request['method'] ) ) { $request['method'] = 'POST'; } $response = FS_Api::remote_request( $url, $request ); if ( 'https://' === substr( $url, 0, 8 ) && FS_Api::is_ssl_error_response( $response ) ) { // Failed due to old version of cURL or Open SSL (SSLv3 is not supported by CloudFlare). $url = 'http://' . substr( $url, 8 ); $request['timeout'] = 15; $response = FS_Api::remote_request( $url, $request ); } if ( false !== $cache_key ) { set_transient( $cache_key, $response, ( ( $response instanceof WP_Error ) ? $failure_cache_expiration : $success_cache_expiration ) ); } } return $response; } /** * This method is used to enrich the after upgrade notice instructions when the upgraded * license cannot be activated network wide (license quota isn't large enough). * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return string */ private function get_license_network_activation_notice() { if ( ! $this->_is_network_active ) { // Module isn't network level activated. return ''; } if ( ! fs_is_network_admin() ) { // Not network level admin. return ''; } if ( get_blog_count() == 1 ) { // There's only a single site in the network so if there's a context license it was already activated. return ''; } if ( ! is_object( $this->_license ) ) { // No context license. return ''; } if ( $this->_license->is_single_site() && 0 < $this->_license->activated ) { // License was already utilized (this is not 100% the case if all the network is localhost sites and the license can be utilized on unlimited localhost sites). return ''; } if ( $this->can_activate_license_on_network( $this->_license ) ) { // License can be activated on all the network, so probably, the license is already activate on all the network (that's how the after upgrade sync works). return ''; } return sprintf( $this->get_text_inline( '%sClick here%s to choose the sites where you\'d like to activate the license on.', 'network-choose-sites-for-license' ), '', '' ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @param string $key * * @return string */ function get_text( $key ) { return fs_text( $key, $this->_slug ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * * @return string */ function get_text_inline( $text, $key = '' ) { return _fs_text_inline( $text, $key, $this->_slug ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $context Context information for the translators. * @param string $key String key for overrides. * * @return string */ function get_text_x_inline( $text, $context, $key ) { return _fs_text_x_inline( $text, $context, $key, $this->_slug ); } /** * @author Vova Feldman (@svovaf) * @since 1.2.3 * * @param string $text Translatable string. * @param string $key String key for overrides. * * @return string */ function esc_html_inline( $text, $key ) { return esc_html( _fs_text_inline( $text, $key, $this->_slug ) ); } #---------------------------------------------------------------------------------- #region Versioning #---------------------------------------------------------------------------------- /** * Check if Freemius in SDK upgrade mode. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_sdk_upgrade_mode() { return isset( $this->_storage->sdk_upgrade_mode ) ? $this->_storage->sdk_upgrade_mode : false; } /** * Turn SDK upgrade mode off. * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ function set_sdk_upgrade_complete() { $this->_storage->sdk_upgrade_mode = false; } /** * Check if plugin upgrade mode. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_plugin_upgrade_mode() { return isset( $this->_storage->plugin_upgrade_mode ) ? $this->_storage->plugin_upgrade_mode : false; } /** * Turn plugin upgrade mode off. * * @author Vova Feldman (@svovaf) * @since 1.0.9 */ function set_plugin_upgrade_complete() { $this->_storage->plugin_upgrade_mode = false; $license_migration = ! empty( $this->_storage->license_migration ) ? $this->_storage->license_migration : array(); $license_migration['is_migrating'] = false; $this->_storage->license_migration = $license_migration; } #endregion #---------------------------------------------------------------------------------- #region Permissions #---------------------------------------------------------------------------------- /** * Check if specific permission requested. * * @author Vova Feldman (@svovaf) * @since 1.1.6 * * @param string $permission * * @return bool */ function is_permission_requested( $permission ) { return isset( $this->_permissions[ $permission ] ) && ( true === $this->_permissions[ $permission ] ); } #endregion #---------------------------------------------------------------------------------- #region Auto Activation #---------------------------------------------------------------------------------- /** * Hints the SDK if running an auto-installation. * * @var bool */ private $_isAutoInstall = false; /** * After upgrade callback to install and auto activate a plugin. * This code will only be executed on explicit request from the user, * following the practice Jetpack are using with their theme installations. * * @link https://make.wordpress.org/plugins/2017/03/16/clarification-of-guideline-8-executable-code-and-installs/ * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 */ function _install_premium_version_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'install_premium_version' ); if ( ! $this->is_registered() ) { // Not registered. self::shoot_ajax_failure( array( 'message' => $this->get_text_inline( 'Auto installation only works for opted-in users.', 'auto-install-error-not-opted-in' ), 'code' => 'premium_installed', ) ); } $plugin_id = fs_request_get( 'target_module_id', $this->get_id() ); if ( ! FS_Plugin::is_valid_id( $plugin_id ) ) { // Invalid ID. self::shoot_ajax_failure( array( 'message' => $this->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ), 'code' => 'invalid_module_id', ) ); } if ( $plugin_id == $this->get_id() ) { if ( $this->is_premium() ) { // Already using the premium code version. self::shoot_ajax_failure( array( 'message' => $this->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ), 'code' => 'premium_installed', ) ); } if ( ! $this->can_use_premium_code() ) { // Don't have access to the premium code. self::shoot_ajax_failure( array( 'message' => $this->get_text_inline( 'You do not have a valid license to access the premium version.', 'auto-install-error-invalid-license' ), 'code' => 'invalid_license', ) ); } if ( ! $this->has_release_on_freemius() ) { // Plugin is a serviceware, no premium code version. self::shoot_ajax_failure( array( 'message' => $this->get_text_inline( 'Plugin is a "Serviceware" which means it does not have a premium code version.', 'auto-install-error-serviceware' ), 'code' => 'premium_version_missing', ) ); } } else { $addon = $this->get_addon( $plugin_id ); if ( ! is_object( $addon ) ) { // Invalid add-on ID. self::shoot_ajax_failure( array( 'message' => $this->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ), 'code' => 'invalid_module_id', ) ); } if ( $this->is_addon_activated( $plugin_id, true ) ) { // Premium add-on version is already activated. self::shoot_ajax_failure( array( 'message' => $this->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ), 'code' => 'premium_installed', ) ); } } $this->_isAutoInstall = true; // Try to install and activate. $updater = FS_Plugin_Updater::instance( $this ); $result = $updater->install_and_activate_plugin( $plugin_id ); if ( is_array( $result ) && ! empty( $result['message'] ) ) { self::shoot_ajax_failure( array( 'message' => $result['message'], 'code' => $result['code'], ) ); } self::shoot_ajax_success( $result ); } /** * Displays module activation dialog box after a successful upgrade * where the user explicitly requested to auto download and install * the premium version. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 */ function _add_auto_installation_dialog_box() { $this->_logger->entrance(); if ( ! $this->is_registered() ) { // Not registered. return; } $plugin_id = fs_request_get( 'plugin_id', $this->get_id() ); if ( ! FS_Plugin::is_valid_id( $plugin_id ) ) { // Invalid module ID. return; } if ( $plugin_id == $this->get_id() ) { if ( $this->is_premium() ) { // Already using the premium code version. return; } if ( ! $this->can_use_premium_code() ) { // Don't have access to the premium code. return; } if ( ! $this->has_release_on_freemius() ) { // Plugin is a serviceware, no premium code version. return; } } else { $addon = $this->get_addon( $plugin_id ); if ( ! is_object( $addon ) ) { // Invalid add-on ID. return; } if ( $this->is_addon_activated( $plugin_id, true ) ) { // Premium add-on version is already activated. return; } } $vars = array( 'id' => $this->_module_id, 'target_module_id' => $plugin_id, 'slug' => $this->_slug, ); fs_require_template( 'auto-installation.php', $vars ); } #endregion #-------------------------------------------------------------------------------- #region Tabs Integration #-------------------------------------------------------------------------------- #region Module's Original Tabs /** * Inject a JavaScript logic to capture the theme tabs HTML. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ function _tabs_capture() { $this->_logger->entrance(); if ( ! $this->is_product_settings_page() || ! $this->should_page_include_tabs() || ! $this->is_matching_url( $this->main_menu_url() ) ) { return; } $params = array( 'id' => $this->_module_id, ); fs_require_once_template( 'tabs-capture-js.php', $params ); } /** * Cache theme's tabs HTML for a week. The cache will also be set as expired * after version and type (free/premium) changes, in addition to the week period. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ function _store_tabs_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'store_tabs' ); // Init filesystem if not yet initiated. WP_Filesystem(); // Get POST body HTML data. global $wp_filesystem; $tabs_html = $wp_filesystem->get_contents( "php://input" ); if ( is_string( $tabs_html ) ) { $tabs_html = trim( $tabs_html ); } if ( ! is_string( $tabs_html ) || empty( $tabs_html ) ) { self::shoot_ajax_failure(); } $this->_cache->set( 'tabs', $tabs_html, 7 * WP_FS__TIME_24_HOURS_IN_SEC ); self::shoot_ajax_success(); } /** * Cache theme's settings page custom styles. The cache will also be set as expired * after version and type (free/premium) changes, in addition to the week period. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ function _store_tabs_styles() { $this->_logger->entrance(); if ( ! $this->is_product_settings_page() || ! $this->should_page_include_tabs() || ! $this->is_matching_url( $this->main_menu_url() ) ) { return; } $wp_styles = wp_styles(); $theme_styles_url = get_template_directory_uri(); $stylesheets = array(); foreach ( $wp_styles->queue as $handler ) { if ( fs_starts_with( $handler, 'fs_' ) ) { // Assume that stylesheets that their handler starts with "fs_" belong to the SDK. continue; } /** * @var _WP_Dependency $stylesheet */ $stylesheet = $wp_styles->registered[ $handler ]; if ( fs_starts_with( $stylesheet->src, $theme_styles_url ) ) { $stylesheets[] = $stylesheet->src; } } if ( ! empty( $stylesheets ) ) { $this->_cache->set( 'tabs_stylesheets', $stylesheets, 7 * WP_FS__TIME_24_HOURS_IN_SEC ); } } /** * Check if module's original settings page has any tabs. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool */ private function has_tabs() { return $this->_cache->has( 'tabs' ); } /** * Get module's settings page HTML content, starting * from the beginning of the
    element, * until the tabs HTML (including). * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return string */ private function get_tabs_html() { $this->_logger->entrance(); return $this->_cache->get( 'tabs' ); } /** * Check if page should include tabs. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool */ private function should_page_include_tabs() { if ( ! $this->has_settings_menu() ) { // Don't add tabs if no settings at all. return false; } if ( self::NAVIGATION_TABS !== $this->_navigation ) { // Only add tabs to themes for now. return false; } if ( $this->is_theme() && ! $this->has_paid_plan() && ! $this->has_addons() ) { // Only add tabs to monetizing themes. return false; } if ( ! $this->is_product_settings_page() ) { // Only add tabs if browsing one of the product's setting pages. return false; } if ( $this->is_activation_mode() && $this->is_activation_page() ) { // Don't include tabs in the activation page. return false; } if ( $this->is_admin_page( 'pricing' ) && fs_request_get_bool( 'checkout' ) ) { // Don't add tabs on checkout page, we want to reduce distractions // as much as possible. return false; } return true; } /** * Add the tabs HTML before the setting's page content and * enqueue any required stylesheets. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool If tabs were included. */ function _add_tabs_before_content() { $this->_logger->entrance(); if ( ! $this->should_page_include_tabs() ) { return false; } $tabs_html = $this->get_tabs_html(); if ( empty( $tabs_html ) ) { return false; } /** * Enqueue the original stylesheets that are included in the * theme settings page. That way, if the theme settings has * some custom _styled_ content above the tabs UI, this * will make sure that the styling is preserved. */ $stylesheets = $this->_cache->get( 'tabs_stylesheets', array() ); if ( is_array( $stylesheets ) ) { for ( $i = 0, $len = count( $stylesheets ); $i < $len; $i ++ ) { wp_enqueue_style( "fs_{$this->_module_id}_tabs_{$i}", $stylesheets[ $i ] ); } } // Cut closing
    tag. echo substr( trim( $tabs_html ), 0, - 6 ); return true; } /** * Add the tabs closing HTML after the setting's page content. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @return bool If tabs closing HTML was included. */ function _add_tabs_after_content() { $this->_logger->entrance(); if ( ! $this->should_page_include_tabs() ) { return false; } echo ''; return true; } #endregion /** * Add in-page JavaScript to inject the Freemius tabs into * the module's setting tabs section. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ function _add_freemius_tabs() { $this->_logger->entrance(); if ( ! $this->should_page_include_tabs() ) { return; } $params = array( 'id' => $this->_module_id ); fs_require_once_template( 'tabs.php', $params ); } #endregion #-------------------------------------------------------------------------------- #region Customizer Integration for Themes #-------------------------------------------------------------------------------- /** * @author Vova Feldman (@svovaf) * @since 1.2.2.7 * * @param WP_Customize_Manager $customizer */ function _customizer_register( $customizer ) { $this->_logger->entrance(); if ( $this->is_pricing_page_visible() ) { require_once WP_FS__DIR_INCLUDES . '/customizer/class-fs-customizer-upsell-control.php'; $customizer->add_section( 'freemius_upsell', array( 'title' => '★ ' . $this->get_text_inline( 'View paid features', 'view-paid-features' ), 'priority' => 1, ) ); $customizer->add_setting( 'freemius_upsell', array( 'sanitize_callback' => 'esc_html', ) ); $customizer->add_control( new FS_Customizer_Upsell_Control( $customizer, 'freemius_upsell', array( 'fs' => $this, 'section' => 'freemius_upsell', 'priority' => 100, ) ) ); } if ( $this->is_page_visible( 'contact' ) || $this->is_page_visible( 'support' ) ) { require_once WP_FS__DIR_INCLUDES . '/customizer/class-fs-customizer-support-section.php'; // Main Documentation Link In Customizer Root. $customizer->add_section( new FS_Customizer_Support_Section( $customizer, 'freemius_support', array( 'fs' => $this, 'priority' => 1000, ) ) ); } } #endregion /** * If the theme has a paid version, add some custom * styling to the theme's premium version (if exists) * to highlight that it's the premium version of the * same theme, making it easier for identification * after the user upgrades and upload it to the site. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ function _style_premium_theme() { $this->_logger->entrance(); if ( ! self::is_themes_page() ) { // Only include in the themes page. return; } if ( ! $this->has_paid_plan() ) { // Only include if has any paid plans. return; } $params = null; fs_require_once_template( '/js/jquery.content-change.php', $params ); $params = array( 'slug' => $this->_slug, 'id' => $this->_module_id, ); fs_require_template( '/js/style-premium-theme.php', $params ); } /** * This method will return the absolute URL of the module's local icon. * * When you are running your plugin or theme on a **localhost** environment, if the icon * is not found in the local assets folder, try to fetch the icon URL from Freemius. If not set and * it's a plugin hosted on WordPress.org, try fetching the icon URL from wordpress.org. * If an icon is found, this method will automatically attempt to download the icon and store it * in /freemius/assets/img/{slug}.{png|jpg|gif|svg}. * * It's important to mention that this method is NOT phoning home since the developer will deploy * the product with the local icon in the assets folder. The download process just simplifies * the process for the developer. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return string */ function get_local_icon_url() { global $fs_active_plugins; /** * @since 1.1.7.5 */ $local_path = $this->apply_filters( 'plugin_icon', false ); if ( is_string( $local_path ) ) { $icons = array( $local_path ); } else { $img_dir = WP_FS__DIR_IMG; // Locate the main assets folder. if ( 1 < count( $fs_active_plugins->plugins ) ) { $plugin_or_theme_img_dir = ( $this->is_plugin() ? WP_PLUGIN_DIR : get_theme_root( get_stylesheet() ) ); foreach ( $fs_active_plugins->plugins as $sdk_path => &$data ) { if ( $data->plugin_path == $this->get_plugin_basename() ) { $img_dir = $plugin_or_theme_img_dir . '/' /** * The basename will be `themes` or the basename of a custom themes directory. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ . str_replace( '../' . basename( $plugin_or_theme_img_dir ) . '/', '', $sdk_path ) . '/assets/img'; break; } } } // Try to locate the icon in the assets folder. $icons = glob( fs_normalize_path( $img_dir . "/{$this->_slug}.*" ) ); if ( ! is_array( $icons ) || 0 === count( $icons ) ) { if ( ! WP_FS__IS_LOCALHOST && $this->is_theme() ) { $icons = array( fs_normalize_path( $img_dir . '/theme-icon.png' ) ); } else { $icon_found = false; $local_path = fs_normalize_path( "{$img_dir}/{$this->_slug}.png" ); if ( ! function_exists( 'get_filesystem_method' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } $have_write_permissions = ( 'direct' === get_filesystem_method( array(), fs_normalize_path( $img_dir ) ) ); /** * IMPORTANT: THIS CODE WILL NEVER RUN AFTER THE PLUGIN IS IN THE REPO. * * This code will only be executed once during the testing * of the plugin in a local environment. The plugin icon file WILL * already exist in the assets folder when the plugin is deployed to * the repository. */ if ( WP_FS__IS_LOCALHOST && $have_write_permissions ) { // Fetch icon from Freemius. $icon = $this->fetch_remote_icon_url(); // Fetch icon from WordPress.org. if ( empty( $icon ) && $this->is_plugin() && $this->is_org_repo_compliant() ) { if ( ! function_exists( 'plugins_api' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; } $plugin_information = plugins_api( 'plugin_information', array( 'slug' => $this->_slug, 'fields' => array( 'sections' => false, 'tags' => false, 'icons' => true ) ) ); if ( ! is_wp_error( $plugin_information ) && isset( $plugin_information->icons ) && ! empty( $plugin_information->icons ) ) { /** * Get the smallest icon. * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ $icon = end( $plugin_information->icons ); } } if ( ! empty( $icon ) ) { if ( 0 !== strpos( $icon, 'http' ) ) { $icon = 'http:' . $icon; } /** * Get a clean file extension, e.g.: "jpg" and not "jpg?rev=1305765". * * @author Leo Fajardo (@leorw) * @since 1.2.2 */ $ext = pathinfo( strtok( $icon, '?' ), PATHINFO_EXTENSION ); $local_path = fs_normalize_path( "{$img_dir}/{$this->_slug}.{$ext}" ); // Try to download the icon. $icon_found = fs_download_image( $icon, $local_path ); } } if ( ! $icon_found ) { // No icons found, fallback to default icon. if ( $have_write_permissions ) { // If have write permissions, copy default icon. copy( fs_normalize_path( $img_dir . "/{$this->_module_type}-icon.png" ), $local_path ); } else { // If doesn't have write permissions, use default icon path. $local_path = fs_normalize_path( $img_dir . "/{$this->_module_type}-icon.png" ); } } $icons = array( $local_path ); } } } $icon_dir = dirname( $icons[0] ); return fs_img_url( substr( $icons[0], strlen( $icon_dir ) ), $icon_dir ); } /** * Fetch module's extended info. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return object|mixed */ private function fetch_module_info() { return $this->get_api_plugin_scope()->get( 'info.json', false, WP_FS__TIME_WEEK_IN_SEC ); } /** * Fetch module's remote icon URL. * * @author Vova Feldman (@svovaf) * @since 2.0.0 * * @return string */ function fetch_remote_icon_url() { $info = $this->fetch_module_info(); return ( $this->is_api_result_object( $info, 'icon' ) && is_string( $info->icon ) ) ? $info->icon : ''; } #-------------------------------------------------------------------------------- #region GDPR #-------------------------------------------------------------------------------- /** * @author Leo Fajardo (@leorw) * @since 2.1.0 * * @param array $user_plugins * * @return string */ private function get_gdpr_admin_notice_string( $user_plugins ) { $this->_logger->entrance(); $addons = self::get_all_addons(); foreach ( $user_plugins as $user_plugin ) { $has_addons = isset( $addons[ $user_plugin->id ] ); if ( WP_FS__MODULE_TYPE_PLUGIN === $user_plugin->type && ! $has_addons ) { if ( $this->_module_id == $user_plugin->id ) { $addons = $this->get_addons(); $has_addons = ( ! empty( $addons ) ); } else { $plugin_api = FS_Api::instance( $user_plugin->id, 'plugin', $user_plugin->id, $user_plugin->public_key, ! $user_plugin->is_live, false, $this->get_sdk_version() ); $addons_result = $plugin_api->get( '/addons.json?enriched=true', true ); if ( $this->is_api_result_object( $addons_result, 'plugins' ) && is_array( $addons_result->plugins ) && ! empty( $addons_result->plugins ) ) { $has_addons = true; } } } $user_plugin->has_addons = $has_addons; } $is_single_parent_product = ( 1 === count( $user_plugins ) ); $multiple_products_text = ''; if ( $is_single_parent_product ) { $single_parent_product = reset( $user_plugins ); $thank_you = sprintf( "%s", $single_parent_product->id, sprintf( $single_parent_product->has_addons ? $this->get_text_inline( 'Thank you so much for using %s and its add-ons!', 'thank-you-for-using-product-and-its-addons' ) : $this->get_text_inline( 'Thank you so much for using %s!', 'thank-you-for-using-product' ), sprintf('%s', $single_parent_product->title) ) ); $already_opted_in = sprintf( $this->get_text_inline( "You've already opted-in to our usage-tracking, which helps us keep improving the %s.", 'already-opted-in-to-product-usage-tracking' ), ( WP_FS__MODULE_TYPE_THEME === $single_parent_product->type ) ? WP_FS__MODULE_TYPE_THEME : WP_FS__MODULE_TYPE_PLUGIN ); } else { $thank_you = $this->get_text_inline( 'Thank you so much for using our products!', 'thank-you-for-using-products' ); $already_opted_in = $this->get_text_inline( "You've already opted-in to our usage-tracking, which helps us keep improving them.", 'already-opted-in-to-products-usage-tracking' ); $products_and_add_ons = ''; foreach ( $user_plugins as $user_plugin ) { if ( ! empty( $products_and_add_ons ) ) { $products_and_add_ons .= ', '; } if ( ! $user_plugin->has_addons ) { $products_and_add_ons .= sprintf( "%s", $user_plugin->id, $user_plugin->title ); } else { $products_and_add_ons .= sprintf( "%s", $user_plugin->id, sprintf( $this->get_text_inline( '%s and its add-ons', 'product-and-its-addons' ), $user_plugin->title ) ); } } $multiple_products_text = sprintf( "%s: %s", $this->get_text_inline( 'Products', 'products' ), $products_and_add_ons ); } $actions = sprintf( '
    • %s - %s
    • %s - %s
    ', sprintf('', $this->get_text_inline( 'Yes', 'yes' ) ), $this->get_text_inline( 'send me security & feature updates, educational content and offers.', 'send-updates' ), sprintf('', $this->get_text_inline( 'No', 'no' ) ), sprintf( $this->get_text_inline( 'do %sNOT%s send me security & feature updates, educational content and offers.', 'do-not-send-updates' ), '', '' ) ); return sprintf( '%s %s %s', $thank_you, $already_opted_in, sprintf( $this->get_text_inline( 'Due to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)', 'due-to-gdpr-compliance-requirements' ), '', '' ) . '

    ' . '' . $this->get_text_inline( "Please let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:", 'contact-for-updates' ) . '' . $actions . ( $is_single_parent_product ? '' : $multiple_products_text ) ); } /** * This method is called for opted-in users to fetch the is_marketing_allowed flag of the user for all the * plugins and themes they've opted in to. * * @author Leo Fajardo (@leorw) * @since 2.1.0 * * @param string $user_email * @param string $license_key * @param array $plugin_ids * @param string|null $license_key * * @return array|false */ private function fetch_user_marketing_flag_status_by_plugins( $user_email, $license_key, $plugin_ids ) { $request = array( 'method' => 'POST', 'body' => array(), 'timeout' => WP_FS__DEBUG_SDK ? 60 : 30, ); if ( is_string( $user_email ) ) { $request['body']['email'] = $user_email; } else { $request['body']['license_key'] = $license_key; } $result = array(); $url = WP_FS__ADDRESS . '/action/service/user_plugin/'; $total_plugin_ids = count( $plugin_ids ); $plugin_ids_count_per_request = 10; for ( $i = 1; $i <= $total_plugin_ids; $i += $plugin_ids_count_per_request ) { $plugin_ids_set = array_slice( $plugin_ids, $i - 1, $plugin_ids_count_per_request ); $request['body']['plugin_ids'] = $plugin_ids_set; $response = self::safe_remote_post( $url, $request, WP_FS__TIME_24_HOURS_IN_SEC, WP_FS__TIME_12_HOURS_IN_SEC ); if ( ! is_wp_error( $response ) ) { $decoded = is_string( $response['body'] ) ? json_decode( $response['body'] ) : null; if ( !is_object($decoded) || !isset($decoded->success) || true !== $decoded->success || !isset( $decoded->data ) || !is_array( $decoded->data ) ) { return false; } $result = array_merge( $result, $decoded->data ); } } return $result; } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 */ function _maybe_show_gdpr_admin_notice() { if ( ! $this->is_user_in_admin() ) { return; } if ( ! $this->should_handle_gdpr_admin_notice() ) { return; } if ( ! $this->is_user_admin() ) { return; } require_once WP_FS__DIR_INCLUDES . '/class-fs-user-lock.php'; $lock = FS_User_Lock::instance(); /** * Try to acquire a 60-sec lock based on the WP user and thread/process ID. */ if ( ! $lock->try_lock( 60 ) ) { return; } /** * @var $current_wp_user WP_User */ $current_wp_user = self::_get_current_wp_user(); /** * @var FS_User $current_fs_user */ $current_fs_user = Freemius::_get_user_by_email( $current_wp_user->user_email ); $ten_years_in_sec = 10 * 365 * WP_FS__TIME_24_HOURS_IN_SEC; if ( ! is_object( $current_fs_user ) ) { // 10-year lock. $lock->lock( $ten_years_in_sec ); return; } $gdpr = FS_GDPR_Manager::instance(); if ( $gdpr->is_opt_in_notice_shown() ) { // 30-day lock. $lock->lock( 30 * WP_FS__TIME_24_HOURS_IN_SEC ); return; } if ( ! $gdpr->should_show_opt_in_notice() ) { // 10-year lock. $lock->lock( $ten_years_in_sec ); return; } $last_time_notice_shown = $gdpr->last_time_notice_was_shown(); $was_notice_shown_before = ( false !== $last_time_notice_shown ); if ( $was_notice_shown_before && 30 * WP_FS__TIME_24_HOURS_IN_SEC > time() - $last_time_notice_shown ) { // If the notice was shown before, show it again after 30 days from the last time it was shown. return; } /** * Find all plugin IDs that were installed by the current admin. */ $plugin_ids_map = self::get_user_opted_in_module_ids_map( $current_fs_user->id ); if ( empty( $plugin_ids_map )) { $lock->lock( $ten_years_in_sec ); return; } $user_plugins = $this->fetch_user_marketing_flag_status_by_plugins( $current_fs_user->email, null, array_keys( $plugin_ids_map ) ); if ( empty( $user_plugins ) ) { $lock->lock( is_array($user_plugins) ? $ten_years_in_sec : // Lock for 24-hours on errors. WP_FS__TIME_24_HOURS_IN_SEC ); return; } $has_unset_marketing_optin = false; foreach ( $user_plugins as $user_plugin ) { if ( true == $user_plugin->is_marketing_allowed ) { unset( $plugin_ids_map[ $user_plugin->plugin_id ] ); } if ( ! $has_unset_marketing_optin && is_null( $user_plugin->is_marketing_allowed ) ) { $has_unset_marketing_optin = true; } } if ( empty( $plugin_ids_map ) || ( $was_notice_shown_before && ! $has_unset_marketing_optin ) ) { $lock->lock( $ten_years_in_sec ); return; } $modules = array_merge( array_values( self::maybe_get_entities_account_option( 'plugins', array() ) ), array_values( self::maybe_get_entities_account_option( 'themes', array() ) ) ); foreach ( $modules as $module ) { if ( ! FS_Plugin::is_valid_id( $module->parent_plugin_id ) && isset( $plugin_ids_map[ $module->id ] ) ) { $plugin_ids_map[ $module->id ] = $module; } } $plugin_title = null; if ( 1 === count( $plugin_ids_map ) ) { $module = reset( $plugin_ids_map ); $plugin_title = $module->title; } $gdpr->add_opt_in_sticky_notice( $this->get_gdpr_admin_notice_string( $plugin_ids_map ), $plugin_title ); $this->add_gdpr_optin_ajax_handler_and_style(); $gdpr->notice_was_just_shown(); // 30-day lock. $lock->lock( 30 * WP_FS__TIME_24_HOURS_IN_SEC ); } /** * Prevents the GDPR opt-in admin notice from being added if the user has already chosen to allow or not allow * marketing. * * @author Leo Fajardo (@leorw) * @since 2.1.0 */ private function disable_opt_in_notice_and_lock_user() { FS_GDPR_Manager::instance()->disable_opt_in_notice(); require_once WP_FS__DIR_INCLUDES . '/class-fs-user-lock.php'; // 10-year lock. FS_User_Lock::instance()->lock( 10 * 365 * WP_FS__TIME_24_HOURS_IN_SEC ); } /** * @author Leo Fajardo (@leorw) * @since 2.5.4 */ static function _add_api_connectivity_notice_handler_js() { fs_require_once_template( 'api-connectivity-message-js.php' ); } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 */ function _add_gdpr_optin_js() { $vars = array( 'id' => $this->_module_id ); fs_require_once_template( 'gdpr-optin-js.php', $vars ); } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 */ function enqueue_gdpr_optin_notice_style() { fs_enqueue_local_style( 'fs_gdpr_optin_notice', '/admin/gdpr-optin-notice.css' ); } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 */ function _maybe_add_gdpr_optin_ajax_handler() { $this->add_ajax_action( 'fetch_is_marketing_required_flag_value', array( &$this, '_fetch_is_marketing_required_flag_value_ajax_action' ) ); if ( FS_GDPR_Manager::instance()->is_opt_in_notice_shown() ) { $this->add_gdpr_optin_ajax_handler_and_style(); } } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 */ function _fetch_is_marketing_required_flag_value_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'fetch_is_marketing_required_flag_value' ); $license_key = fs_request_get_raw( 'license_key' ); if ( empty($license_key) ) { self::shoot_ajax_failure( $this->get_text_inline( 'License key is empty.', 'empty-license-key' ) ); } $user_plugins = $this->fetch_user_marketing_flag_status_by_plugins( null, $license_key, array( $this->_module_id ) ); if ( ! is_array( $user_plugins ) || empty($user_plugins) || !isset($user_plugins[0]->plugin_id) || $user_plugins[0]->plugin_id != $this->_module_id ) { /** * If faced an error or if the module ID do not match to the current module, ask for GDPR opt-in. * * @author Vova Feldman (@svovaf) */ self::shoot_ajax_success( array( 'is_marketing_allowed' => null, 'license_owner_id' => null ) ); } self::shoot_ajax_success( array( 'is_marketing_allowed' => $user_plugins[0]->is_marketing_allowed, 'license_owner_id' => ( isset( $user_plugins[0]->license_owner_id ) ? $user_plugins[0]->license_owner_id : null ) ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.3.2 * * @param number[] $install_ids * * @return array { * An array of objects containing the installs' licenses owners data. * * @property number $id User ID. * @property string $email User email (can be masked email). * } */ private function fetch_installs_licenses_owners_data( $install_ids ) { $this->_logger->entrance(); $response = $this->get_api_user_scope()->get( '/licenses_owners.json?install_ids=' . implode( ',', $install_ids ) ); $license_owners = array(); if ( $this->is_api_result_object( $response, 'owners' ) ) { $license_owners = $response->owners; } return $license_owners; } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 */ private function add_gdpr_optin_ajax_handler_and_style() { // Add GDPR action AJAX callback. $this->add_ajax_action( 'gdpr_optin_action', array( &$this, '_gdpr_optin_ajax_action' ) ); add_action( 'admin_footer', array( &$this, '_add_gdpr_optin_js' ) ); add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_gdpr_optin_notice_style' ) ); } /** * @author Leo Fajardo (@leorw) * @since 2.1.0 */ function _gdpr_optin_ajax_action() { $this->_logger->entrance(); $this->check_ajax_referer( 'gdpr_optin_action' ); if ( ! fs_request_has( 'is_marketing_allowed' ) || ! fs_request_has( 'plugin_ids' ) ) { self::shoot_ajax_failure(); } $current_wp_user = self::_get_current_wp_user(); $plugin_ids = fs_request_get( 'plugin_ids', array() ); if ( ! is_array( $plugin_ids ) || empty( $plugin_ids ) ) { self::shoot_ajax_failure(); } $modules = array_merge( array_values( self::maybe_get_entities_account_option( 'plugins', array() ) ), array_values( self::maybe_get_entities_account_option( 'themes', array() ) ) ); foreach ( $modules as $key => $module ) { if ( ! in_array( $module->id, $plugin_ids ) ) { unset( $modules[ $key ] ); } } if ( empty( $modules ) ) { self::shoot_ajax_failure(); } $user_api = $this->get_api_user_scope_by_user( Freemius::_get_user_by_email( $current_wp_user->user_email ) ); foreach ( $modules as $module ) { $user_api->call( "?plugin_id={$module->id}", 'put', array( 'is_marketing_allowed' => ( true == fs_request_get_bool( 'is_marketing_allowed' ) ) ) ); } FS_GDPR_Manager::instance()->remove_opt_in_notice(); require_once WP_FS__DIR_INCLUDES . '/class-fs-user-lock.php'; // 10-year lock. FS_User_Lock::instance()->lock( 10 * 365 * WP_FS__TIME_24_HOURS_IN_SEC ); self::shoot_ajax_success(); } /** * Checks if the GDPR admin notice should be handled. By default, this logic is off, unless the integrator adds the special 'handle_gdpr_admin_notice' filter. * * @author Vova Feldman (@svovaf) * @since 2.1.0 * * @return bool */ private function should_handle_gdpr_admin_notice() { return $this->apply_filters( 'handle_gdpr_admin_notice', // Default to false. false ); } #endregion #---------------------------------------------------------------------------------- #region Marketing #---------------------------------------------------------------------------------- /** * Check if current user purchased any other plugins before. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function has_purchased_before() { // TODO: Implement has_purchased_before() method. throw new Exception( 'not implemented' ); } /** * Check if current user classified as an agency. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_agency() { // TODO: Implement is_agency() method. throw new Exception( 'not implemented' ); } /** * Check if current user classified as a developer. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_developer() { // TODO: Implement is_developer() method. throw new Exception( 'not implemented' ); } /** * Check if current user classified as a business. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ function is_business() { // TODO: Implement is_business() method. throw new Exception( 'not implemented' ); } #endregion #---------------------------------------------------------------------------------- #region Helper #---------------------------------------------------------------------------------- /** * If running with a secret key, assume it's the developer and show pending plans as well. * * @author Vova Feldman (@svovaf) * @since 2.1.2 * * @param string $path * * @return string */ function add_show_pending( $path ) { if ( ! $this->has_secret_key() ) { return $path; } return $path . ( false !== strpos( $path, '?' ) ? '&' : '?' ) . 'show_pending=true'; } #endregion } freemius/includes/class-freemius-abstract.php000064400000027761147600046700015446 0ustar00is_registered() && $fs->is_tracking_allowed()` * * @since 1.0.1 * * @param bool $ignore_anonymous_state Since 2.5.1 * * @return bool */ abstract function is_registered( $ignore_anonymous_state = false ); /** * Check if the user skipped connecting the account with Freemius. * * @since 1.0.7 * * @return bool */ abstract function is_anonymous(); /** * Check if the user currently in activation mode. * * @since 1.0.7 * * @return bool */ abstract function is_activation_mode(); #endregion #---------------------------------------------------------------------------------- #region Module Type #---------------------------------------------------------------------------------- /** * Checks if the plugin's type is "plugin". The other type is "theme". * * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return bool */ abstract function is_plugin(); /** * Checks if the module type is "theme". The other type is "plugin". * * @author Leo Fajardo (@leorw) * @since 1.2.2 * * @return bool */ function is_theme() { return ( ! $this->is_plugin() ); } #endregion #---------------------------------------------------------------------------------- #region Permissions #---------------------------------------------------------------------------------- /** * Check if plugin must be WordPress.org compliant. * * @since 1.0.7 * * @return bool */ abstract function is_org_repo_compliant(); /** * Check if plugin is allowed to install executable files. * * @author Vova Feldman (@svovaf) * @since 1.0.5 * * @return bool */ function is_allowed_to_install() { return ( $this->is_premium() || ! $this->is_org_repo_compliant() ); } #endregion /** * Check if user in trial or in free plan (not paying). * * @author Vova Feldman (@svovaf) * @since 1.0.4 * * @return bool */ function is_not_paying() { return ( $this->is_trial() || $this->is_free_plan() ); } /** * Check if the user has an activated and valid paid license on current plugin's install. * * @since 1.0.9 * * @return bool */ abstract function is_paying(); /** * Check if the user is paying or in trial. * * @since 1.0.9 * * @return bool */ function is_paying_or_trial() { return ( $this->is_paying() || $this->is_trial() ); } /** * Check if user in a trial or have feature enabled license. * * @author Vova Feldman (@svovaf) * @since 1.1.7 * * @return bool */ abstract function can_use_premium_code(); #---------------------------------------------------------------------------------- #region Premium Only #---------------------------------------------------------------------------------- /** * All logic wrapped in methods with "__premium_only()" suffix will be only * included in the premium code. * * Example: * if ( freemius()->is__premium_only() ) { * ... * } */ /** * Returns true when running premium plugin code. * * @since 1.0.9 * * @return bool */ function is__premium_only() { return $this->is_premium(); } /** * Check if the user has an activated and valid paid license on current plugin's install. * * @since 1.0.9 * * @return bool * */ function is_paying__premium_only() { return ( $this->is__premium_only() && $this->is_paying() ); } /** * All code wrapped in this statement will be only included in the premium code. * * @since 1.0.9 * * @param string $plan Plan name. * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans. * * @return bool */ function is_plan__premium_only( $plan, $exact = false ) { return ( $this->is_premium() && $this->is_plan( $plan, $exact ) ); } /** * Check if plan matches active license' plan or active trial license' plan. * * All code wrapped in this statement will be only included in the premium code. * * @since 1.0.9 * * @param string $plan Plan name. * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans. * * @return bool */ function is_plan_or_trial__premium_only( $plan, $exact = false ) { return ( $this->is_premium() && $this->is_plan_or_trial( $plan, $exact ) ); } /** * Check if the user is paying or in trial. * * All code wrapped in this statement will be only included in the premium code. * * @since 1.0.9 * * @return bool */ function is_paying_or_trial__premium_only() { return $this->is_premium() && $this->is_paying_or_trial(); } /** * Check if the user has an activated and valid paid license on current plugin's install. * * @since 1.0.4 * * @return bool * * @deprecated Method name is confusing since it's not clear from the name the code will be removed. * @using Alias to is_paying__premium_only() */ function is_paying__fs__() { return $this->is_paying__premium_only(); } /** * Check if user in a trial or have feature enabled license. * * All code wrapped in this statement will be only included in the premium code. * * @author Vova Feldman (@svovaf) * @since 1.1.9 * * @return bool */ function can_use_premium_code__premium_only() { return $this->is_premium() && $this->can_use_premium_code(); } #endregion #---------------------------------------------------------------------------------- #region Trial #---------------------------------------------------------------------------------- /** * Check if the user in a trial. * * @since 1.0.3 * * @return bool */ abstract function is_trial(); /** * Check if trial already utilized. * * @since 1.0.9 * * @return bool */ abstract function is_trial_utilized(); #endregion #---------------------------------------------------------------------------------- #region Plans #---------------------------------------------------------------------------------- /** * Check if the user is on the free plan of the product. * * @since 1.0.4 * * @return bool */ abstract function is_free_plan(); /** * @since 1.0.2 * * @param string $plan Plan name. * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans. * * @return bool */ abstract function is_plan( $plan, $exact = false ); /** * Check if plan based on trial. If not in trial mode, should return false. * * @since 1.0.9 * * @param string $plan Plan name. * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans. * * @return bool */ abstract function is_trial_plan( $plan, $exact = false ); /** * Check if plan matches active license' plan or active trial license' plan. * * @since 1.0.9 * * @param string $plan Plan name. * @param bool $exact If true, looks for exact plan. If false, also check "higher" plans. * * @return bool */ function is_plan_or_trial( $plan, $exact = false ) { return $this->is_plan( $plan, $exact ) || $this->is_trial_plan( $plan, $exact ); } /** * Check if plugin has any paid plans. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return bool */ abstract function has_paid_plan(); /** * Check if plugin has any free plan, or is it premium only. * * Note: If no plans configured, assume plugin is free. * * @author Vova Feldman (@svovaf) * @since 1.0.7 * * @return bool */ abstract function has_free_plan(); /** * Check if plugin is premium only (no free plans). * * NOTE: is__premium_only() is very different method, don't get confused. * * @author Vova Feldman (@svovaf) * @since 1.1.9 * * @return bool */ abstract function is_only_premium(); /** * Check if module has a premium code version. * * Serviceware module might be freemium without any * premium code version, where the paid features * are all part of the service. * * @author Vova Feldman (@svovaf) * @since 1.2.1.6 * * @return bool */ abstract function has_premium_version(); /** * Check if module has any release on Freemius, * or all plugin's code is on WordPress.org (Serviceware). * * @return bool */ function has_release_on_freemius() { return ! $this->is_org_repo_compliant() || $this->has_premium_version(); } /** * Checks if it's a freemium plugin. * * @author Vova Feldman (@svovaf) * @since 1.1.9 * * @return bool */ function is_freemium() { return $this->has_paid_plan() && $this->has_free_plan(); } /** * Check if module has only one plan. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 * * @return bool */ abstract function is_single_plan(); #endregion /** * Check if running payments in sandbox mode. * * @since 1.0.4 * * @return bool */ abstract function is_payments_sandbox(); /** * Check if running test vs. live plugin. * * @since 1.0.5 * * @return bool */ abstract function is_live(); /** * Check if running premium plugin code. * * @since 1.0.5 * * @return bool */ abstract function is_premium(); /** * Get upgrade URL. * * @author Vova Feldman (@svovaf) * @since 1.0.2 * * @param string $period Billing cycle. * * @return string */ abstract function get_upgrade_url( $period = WP_FS__PERIOD_ANNUALLY ); /** * Check if Freemius was first added in a plugin update. * * @author Vova Feldman (@svovaf) * @since 1.1.5 * * @return bool */ function is_plugin_update() { return ! $this->is_plugin_new_install(); } /** * Check if Freemius was part of the plugin when the user installed it first. * * @author Vova Feldman (@svovaf) * @since 1.1.5 * * @return bool */ abstract function is_plugin_new_install(); #---------------------------------------------------------------------------------- #region Marketing #---------------------------------------------------------------------------------- /** * Check if current user purchased any other plugins before. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ abstract function has_purchased_before(); /** * Check if current user classified as an agency. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ abstract function is_agency(); /** * Check if current user classified as a developer. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ abstract function is_developer(); /** * Check if current user classified as a business. * * @author Vova Feldman (@svovaf) * @since 1.0.9 * * @return bool */ abstract function is_business(); #endregion }freemius/languages/index.php000064400000000127147600046700012155 0ustar009aE9+909::-:<:D:X:]:e:x:::: : :: :E:y;;;;; X<c<w< < < <<<Y<7=F= W= c=o=x=}== =(=1=% >:2>m> r>>>> > >>> >7>!? &?1?xD?? R@ _@i@@A*A2A5BAxAXAtAaB{BBBBBBB C CCrD[DfDGE ME[EpEEEYE_Ea\FFFFG G G !G;/GkGpGwGOvHH H H HHjHjI5yI II3I2I)J~=J:JUJMKiKKK)K-K LSLsL'LLgLMMZlM7M>Mm>NgNpOOOyOP8P XPdPwP PP%P PQQ-QDQ bQ&lQQ1)R.[RORRAZSSyS26TiTTaT UU%UB)U,lUU U UUU UUV V V &V2V BVNVdV4mVV VVVVVVVVW W(W /W ;WGWaW,fW W WWWnXrX X!XX XXX%X Y%Y+8YdY |Y Y/YYmY~9ZZZZi[q\\ \\ \ \)\.\V]fq]|]U^r^4{^^5^(^__-3_a_Uu_6_`1`~`|a[bbcc +c5c(Dc*mc"c1c+c*d&DdNkdddd.d)e9eIeieqee e eeeee ee ee ffW*f fffffffggggg .g:g Lg5Wgsglh&nhhh h hiiij(j0jLj Rj\j aj=mj&jLjfkk k kkkk kk k kk k l !l.l?ll%lm/mm)m n n\"nnnn6oKo^oC}oooopmpxqd|q-qr r"r)r'HrprMsGks,ss sttStuu4vw"w'w-wE2wxwwwwww*wx* x^7xxxxxxdx'y 0y=y Vydywyyyyy yyzizWznzH{"{B|6S|=|| ||-|*}H}&^}/}}#}*}~"~+~/I~Qy~U~D!$fM/o;> &"<IDZˁO&vT~RӃ.&.U-;ą9Z:3<ɆbPiU_pK (WG#ȉ%)$<aU)Xɋ;ދ6>Qь$ &B^&|*7΍;3Tˎߎ*1'Yu#ɏۏ .CW\ anNwƐ.?PU[1vđԑ 7 @ N9ZCؒQݒ/ ? LVp  ɓ ӓ ߓ    '4:3Qԙ&S-c '3<Q,\$-Lg  ǝ֝͝:ޝ G:7V̞v#JNU^gn{ #2E-U0ǡΡݡ  3S Zf mz ʢYڢ4;<<:P ۤ$$ 2.K,z$O'2-Z  Ѧ ަ   &3I<Yh!!(: ̨ ب  'E4z ©ԩ ),=jqx  ̪ Ӫ9ު&\6l  Ȭ+լH`]׭)AT[ pM}V˯ "/B[nu;`ǰf(ѱ ر '$+B 7 A N [h[xԳ/")Akp:i0ϵ!9\Q#X9@dz2߷ETXUkoa (5H`,! #D$W|&ENdu0)Zkv6/GB Q*.5H^}  ʿڿ6Qgn u  9 W dq$+ ;&E l *! ( 5)?iYme - :G#  ),3 `m }!8 /Caj'?0+!u P1  Nls !   !( / <IMT d q~L0FY` v  (]]O$   )3R Yc hAt$`i<    !+M `zm u,'  _ w/>2V a^h{K0N Ubi9,9f% zEbwGmt{6  *DKjo  W .=TmlQeZ%/6;F0"S n!4SD[5G:&Mtg0%:W2o7MP(yVW#2&V }#D)O$k4TWZrQJ!8Lk#IX   6=D MZ^bip w    '.>ELS\ov} F #*18 ?IP W d nx  3?#* .; B LYt{  %s to access version %s security & feature updates, and support. The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box. The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s"The ", e.g.: "The plugin"The %s's%1$s has been placed into safe mode because we noticed that %2$s is an exact copy of %3$s.%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s automatic security & feature updates and paid functionality will keep working without interruptions until %s (or when your license expires, whatever comes first).%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is my client's email address%s is my email address%s is the new owner of the account.%s minimum payout amount.%s opt-in was successfully completed.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s to access version %s security & feature updates, and support.%s to activate the license once you get it.%s tracking cookie after the first visit to maximize earnings potential.%s's paid features%sClick here%s to choose the sites where you'd like to activate the license on.Click here to learn more about updating PHP.A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours.A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder.APIAPI connectivity state is unknownUnknownASCII arrow left icon←ASCII arrow right icon➤AccountAccount DetailsAccount is pending activation. Please check your email and click the link to activate your account and then submit the affiliate form again.ActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.An unknown error has occurred while trying to set the user's beta mode.An unknown error has occurred while trying to toggle the license's white-label mode.An unknown error has occurred.An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre both %s and %s your email addresses?Are you sure you want to delete all Freemius data?Are you sure you want to proceed?Are you sure you would like to proceed with the disconnection?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Associate with the license owner's account.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBetaBillingBilling & InvoicesBlockingBlog IDBodyBundleBundle PlanBusiness nameBuy a license nowBuy licenseBy changing the user, you agree to transfer the account ownership to:By disconnecting the website, previously shared diagnostic data about %1$s will be deleted and no longer visible to %2$s.Can't find your license key?CancelCancel %s & ProceedCancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site.Cancel %s?Cancel InstallationCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanChange UserCheckoutCityClear API CacheClear Updates TransientsClick hereClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCommunicationCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeCurrent %s & SDK versions, and if active or uninstalledDateDeactivateDeactivate LicenseDeactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site.Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDebug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the "Stop Debug" link.Delegate to Site AdminsDelete All AccountsDetailsDiagnostic InfoDiagnostic data will no longer be sent from %s to %s.Disabling white-label modeDisconnecting the website will permanently remove %s from your User Dashboard's account.Don't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support.Don't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload Paid VersionDownload the latest %s versionDownload the latest versionDownloadedDue to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.Duplicate WebsiteDuring the update process we detected %d site(s) that are still pending license activation.During the update process we detected %s site(s) in the network that are still pending your attention.EmailEmail addressEmail address updateEnabling white-label modeEndEnter email addressEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used during the purchase and we will resend you the license key.Enter the email address you've used for the upgrade below and we will resend you the license key.Enter the new email addressErrorError received from the server:ExpiredExpires in %sExtensionsExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.For delivery of security & feature updates, and license management, %s needs toFreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFreemius is our licensing and software updates engineFull nameFunctionGet commission for automated subscription renewals.Get updates for bleeding edge Beta versions of %s.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!Homepage URL & title, WP & PHP versions, and site languageHow do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I Agree - Change UserI can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf this is a long term duplicate, to keep automatic updates and paid functionality after %s, please %s.If you click it, this decision will be delegated to the sites administrators.If you didn't get the email, try checking your spam folder or search for emails from %4$s.If you have a moment, please let us know why you are %sIf you skip this, that's okay! %1$s will still work just fine.If you wish to cancel your %1$s plan's subscription instead, please navigate to the %2$s and cancel it there.If you would like to give up the ownership of the %s's account to %s click the Change Ownership button.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.Important Upgrade Notice:In %sIn case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?Install Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid clone resolution action.Invalid module ID.Invalid new user ID or email address.Invalid site details collection.InvoiceIs %2$s a duplicate of %4$s?Is %2$s a new website?Is %2$s the new home of %4$s?Is ActiveIs active, deactivated, or uninstalledIs this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin.It looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It requires license activation.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's a temporary %s - I'm troubleshooting an issueIt's not what I was looking forJoin the Beta programJust letting you know that the add-ons information of %s is being pulled from an external server.Keep SharingKeep automatic updatesKeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense IDLicense KeyLicense issues?License keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerLong-Term DuplicateMessageMethodMigrateMigrate LicenseMigrate Options to NetworkMobile appsModuleModule PathModule TypeMore information about %sNameNames, slugs, versions, and if active or notNetwork BlogNetwork UserNever miss an important updateNever miss important updates, get security warnings before they become public knowledge, and receive notifications about special offers and awesome new features.NewNew Version AvailableNew WebsiteNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo - only move this site's data to %sNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license.Opt InOpt OutOpt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info.Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info. This will help us make the %s more compatible with your site and better at doing what you need it to.Opt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.Part of an activation link message.Click herePart of the message telling the user what they should receive via email.a license keyPart of the message telling the user what they should receive via email.the installation instructionsPart of the message that tells the user to check their spam folder for a specific email.the product's support email addressPayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please enter the license key to enable the debug mode:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.Please provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProceedProcess IDProcessingProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicensePurchase MoreQuick FeedbackQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRenew your license nowRequestsRequires PHP VersionRequires WordPress VersionReset Deactivation SnoozingResultSDKSDK PathSave %sSavedScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes.Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSharing diagnostic data with %s helps to provide functionality that's more relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the plugin should be translated and tailored to.Show error detailsSimulate Network UpgradeSimulate Trial PromotionSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSnooze & %sSo you can reuse the license when the %s is no longer active.Social media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart DebugStart TrialStart my free %sStateStay ConnectedStop DebugSubmitSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you for updating to %1$s v%2$s!Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.Thanks for upgrading.Thanks!The %1$s will be periodically sending essential license data to %2$s to check for security and feature updates, and verify the validity of your license.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe following products'The installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The products below have been placed into safe mode because we noticed that %2$s is an exact copy of %3$s:%1$sThe products below have been placed into safe mode because we noticed that %2$s is an exact copy of these sites:%3$s%1$sThe remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a %s of %s available.There is a new version of %s available.There was an unexpected API error while processing your request. Please try again in a few minutes and if it still doesn't work, contact the %s's author with the following:This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.This plugin requires a newer version of PHP.This will allow %s toTimestampTitleTo avoid breaking your website due to WordPress or PHP version incompatibilities, and recognize which languages & regions the %s should be translated and tailored to.To ensure compatibility and avoid conflicts with your installed plugins and themes.To enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your "My Profile" section of your User Dashboard:To let you manage & control where the license is activated and ensure %s security & feature updates are only delivered to websites you authorize.To provide additional functionality that's relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the %s should be translated and tailored to.TotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser DashboardUser IDUser keyUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View %s StateView Basic %s InfoView Basic Profile InfoView Basic Website InfoView Diagnostic InfoView License EssentialsView Plugins & Themes ListView detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We couldn't load the add-ons list. It's probably an issue on our side, please try to come back in few minutes.We have introduced this opt-in so you never miss an important update and help us make the %s more compatible with your site and better at doing what you need it to.We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)Welcome to %s! To get started, please enter your license key:What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress & PHP versions, site language & titleWordPress.org Plugin PageWould you like to merge %s into %s?Would you like to proceed with the update?YesYes - %sYes - both addresses are mineYes - move all my data and assets from %s to %sYes, %%2$s is replacing %%4$s. I would like to migrate my %s from %%4$s to %%2$s.Yes, %2$s is a duplicate of %4$s for the purpose of testing, staging, or development.Yes, %2$s is a new and different website that is separate from %4$s.You already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.You do not have a valid license to access the premium version.You have a %s license.You have purchased a %s license.You have successfully updated your %s.You marked this website, %s, as a temporary duplicate of %s.You marked this website, %s, as a temporary duplicate of these sitesYou might have missed it, but you don't have to share any data and can just %s the opt-in.You should receive %3$s for %1$s to your mailbox at %2$s in the next 5 minutes.You should receive a confirmation email for %1$s to your mailbox at %2$s. Please make sure you click the button in that email to %3$s.You should receive a confirmation email for %s to your mailbox at %s. Please make sure you click the button in that email to %s.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s.Your %s license was successfully deactivated.Your WordPress user's: first & last name, and email addressYour account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully activated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your server is blocking the access to Freemius' API, which is crucial for %1$s synchronization. Please contact your host to whitelist the following domains:%2$sYour subscription was successfully cancelled. Your %s plan license will expire in %s.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onactivate a license hereactive add-onActiveaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismisscomplete the opt-indatadaysdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,hourhoursinstalled add-onInstalledinterjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew Beta versionnew versionnot verifiednounPricenounPricingoptionalproduct versionVersionproductsrevert it nowsecondssecseems like the key you entered doesn't match our records.send me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe above-mentioned sitesthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: linn Yu <977869645@qq.com>, 2023 Language-Team: Chinese (China) (http://app.transifex.com/freemius/wordpress-sdk/language/zh_CN/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: zh_CN Plural-Forms: nplurals=1; plural=0; X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 %s以访问版本%s的安全和功能更新以及支持。%s 的 %s 下载链接 %s、许可证密钥和安装说明已发送到 %s。如果您在 5 分钟后找不到该电子邮件,请检查您的垃圾邮件箱。已安装%1$s的付费版本。请激活它以开始使用%2$s功能。%3$s公司%s 的%1$s 已进入安全模式,因为我们注意到 %2$s 是 %3$s 的精确副本。%1$s将立即停止所有未来的定期付款,并且您的%2$s计划许可证将在%3$s过期。立即完成”%s“激活已成功购买%s加载项。%s安装%s许可证%s以前%s及其附加组件%s 自动安全和功能更新以及付费功能将继续运行而不会中断,直到 %s(或当您的许可证到期时,无论先到者)。%s客户购买新许可证时收取佣金。%s免费试用已成功取消。因为附加组件是高级的,所以它被自动停用。如果你想在将来使用它,你必须购买许可证。%s是一款仅限高级的附加组件。在激活插件之前,必须先购买许可证。%s 是我客户的电子邮件地址%s 是我的电子邮件地址%s是帐户的新主人。%s最低付款金额。%s选择加入已成功完成。%s或更高%s级%s评级%s秒%s星%s星%s时间%年代%s以访问版本%s的安全和功能更新以及支持。%s获得许可证后激活它。%s首次访问后跟踪Cookie,以最大限度地提高收益潜力。%s的付费功能%s单击此处%s可选择要激活许可证的站点。单击此处了解有关更新 PHP 的更多信息。刚刚向 %s 发送了一封确认电子邮件。电子邮件所有者必须在接下来的 4 小时内确认更新。刚刚向 %s 发送了一封确认电子邮件。您必须在接下来的 4 小时内确认更新。如果您找不到该电子邮件,请检查您的垃圾邮件文件夹。API未知←➤帐户帐户详细信息帐户正在等待激活。请检查您的电子邮件并单击链接激活您的帐户,然后再次提交会员表格。行动激活激活%s激活%s计划激活%s功能激活免费版本激活许可证在所有挂起的站点上激活许可证。在网络中的所有站点上激活许可证。激活此加载项激活%s的加载项模块%s的加载项添加另一个域附加组件附加组件加载项必须部署到WordPress.org或Freemius。网址地址行%d会员推广联盟免费%s后,只需支付%s同意并激活许可证所有请求所有类型允许并继续或者,您可以暂时跳过它,稍后在%s的网络级帐户页中激活许可证。数量从%s自动下载并安装的%s(付费版本)将在%s中启动。如果要手动执行此操作,请单击“取消”按钮。尝试设置用户的测试模式时发生了未知错误。尝试切换许可证的白标模式时发生未知错误。发生未知错误。测试版的更新将用最新的测试版替换您安装的%s版本-请小心使用,不要在生产站点上使用。你被警告了。匿名反馈在所有挂起的站点上应用。应用于网络中的所有站点。申请成为推广联盟%s 和 %s 都是您的电子邮件地址吗?是否确实要删除所有Freemius数据?确定要继续吗?您确定要继续断开连接吗?由于我们保留30天的潜在退款,我们只支付超过30天的佣金。与许可证所有者的帐户关联。自动安装仅适用于已选择的用户。在%s中自动续订自动安装平均评级真棒加入推广联盟测试版本开具账单账单和发票阻止博客ID身体批量捆绑计划企业名称立即购买许可证购买许可证通过更改用户,您同意将帐户所有权转让给:断开网站连接后,之前共享的关于 %1$s 的诊断数据将被删除,%2$s 将不再可见。找不到您的许可证密钥?取消取消%s并继续取消%s-我不再需要任何安全和功能更新,也不需要%s的支持服务,因为我不打算在此或任何其他站点上使用%s。取消%s?取消安装取消订阅取消试用取消正在取消%s正在取消%s...取消订阅取消试用将立即阻止使用所有高级功能。你确定吗?更改许可证变更所有权变更计划更改用户结帐城市清除 API 缓存清除更新瞬态点击这里单击此处匿名使用插件单击此处可查看评分为%s的评论单击此处可查看全尺寸屏幕截图%d产品代码沟通兼容至联系方式联系支持联系我们贡献者无法激活%s。国家Cron类型当前的 %s 和 SDK 版本,以及是否激活或卸载日期停用停用许可证停用或卸载%s将自动禁用许可证,您可以在其他站点上使用该许可证。停用许可证将阻止所有高级功能,但将在其他站点上激活许可证。确定要继续吗?停用调试日志调试模式已成功启用,将在60分钟以内自动禁用。您也可以通过单击“停止调试”链接提前禁用。委派给站点管理员删除所有帐户细节诊断信息诊断数据将不再从 %s 发送到 %s。禁用白标模式断开网站连接将从您的用户仪表板帐户中永久删除 %s。不要取消%s-我仍然对获取安全和功能更新感兴趣,也可以联系支持人员。没有许可证密钥?捐赠给这个插件降低您的计划下载下载%s版本下载付费版本下载最新的%s版本下载最新版本下载由于新的%sEU通用数据保护条例(GDPR)%s的合规性要求,要求您再次明确表示同意,确认您已经加入:-)由于违反我们的推广联盟条款,我们决定暂时封锁您的附属帐户。如果您有任何问题,请联系支持人员。复制网站在更新过程中,我们检测到%d个网站仍在等待许可证激活。在更新过程中,我们在网络中检测到%s个网站仍然需要您的注意。电子邮件电子邮件地址电子邮件地址更新启用白标模式终点请输入邮箱地址请输入您计划升级%s的网站或其他网站的域。输入您在购买时使用的电子邮件地址,我们将重新向您发送许可证密钥。请在下面输入您用于升级的电子邮件地址,我们将重新向您发送许可证密钥。输入新的电子邮件地址错误从服务器收到错误:期满%s后过期扩展额外域您将营销产品的额外的网域。文件过滤为了遵守WordPress.org指南,在开始试用之前,我们要求您选择使用您的用户和非敏感站点信息,允许%s定期向%s发送数据以检查版本更新并验证您的试用。为了提供安全和功能更新以及许可证管理,%s 需要免费的免费试用免费版本Freemius APIFreemius 调试Freemius SDK找不到插件的主文件。请关于当前错误与sdk@freemius.com联系。Freemius 状态Freemius是我们的许可和软件更新引擎全名函数获得自动续订的佣金。获取%s的前沿测试版本的更新。有许可证密钥吗?嘿,你知道%s有会员计划吗?如果您喜欢%s,您可以成为我们的大使并赚取一些现金!主页 URL 和标题、WP 和 PHP 版本以及站点语言到目前为止,您觉得%s怎么样?使用%d天免费试用来测试我们所有的%s高级功能。如何上传和激活?您将如何推广我们?我同意 - 更改用户我再也付不起了我不明白该怎么做我不想和你分享我的信息我找到了更好的%s我已经升级了我的帐户,但是当我尝试同步许可证时,计划仍然是%s。我不再需要%s我只需要在短时间内使用%sID如果这是长期副本,要在 %s 之后保留自动更新和付费功能,请 %s。如果单击它,此决定将委派给站点管理员。如果您没有收到电子邮件,请尝试检查您的垃圾邮件文件夹或搜索来自%4$s。如果您有空,请告诉我们您为什么是%s如果你跳过这个,没关系! %1$s 仍然可以正常工作。如果您希望取消 %1$s 计划的订阅,请导航至 %2$s 并在那里取消。如果要将%s帐户的所有权放弃给%s,请单击“更改所有权”按钮。如果要在这些站点上使用%s,请在下面输入许可证密钥,然后单击“激活”按钮。重要升级通知:在%s中如果您不打算在此站点(或任何其他站点)上使用此%s,是否也要取消%s?立即安装免费版本立即安装免费版本更新立即安装立即安装更新正在安装插件:%s无效的克隆解析操作。模块ID无效。无效的新用户ID或电子邮件地址。无效网站详细信息集合。发票%2$s 是 %4$s 的副本吗?%2$s 是一个新网站吗?%2$s 是 %4$s 的新主页吗?处于活动状态处于激活、已停用或已卸载这是您客户的网站吗?如果您希望从WP管理员那里隐藏敏感信息,例如电子邮件,许可证密钥,价格,账单地址和发票,则为%s。似乎无法激活许可证。许可证停用似乎失败。看起来您不再处于试用模式,所以没有什么需要取消的:)看来你还在%s计划中。如果你确实升级或更改了计划,这可能是我们这边的问题-对不起。您的站点当前似乎没有活动许可证。它需要许可证激活。似乎身份验证参数中的一个不正确。请更新您的公钥、密钥和用户ID,然后重试。这是一个临时的 %s - 我正在解决一个问题这不是我要找的加入测试计划只是让您知道%s的加载项信息是从外部服务器提取的。继续分享保持自动更新钥匙请分享什么地方出了错,以便我们可以为未来的用户修复它...请告诉我们原因以便我们改进。最后上次更新时间最后一个许可证已安装最新的免费版本已安装最新版本了解更多长度许可证许可协议许可证ID许可证密钥许可问题?许可证密钥许可证密钥为空。终身喜欢%s吗?成为我们的大使并赚取现金 ;-)加载数据库选项本机日志记录器长期副本消息方法迁移迁移许可证将选项迁移到网络移动应用程序模块模块路径模块类型有关%s的详细信息姓名名称、别名、版本,以及是否处于激活状态网络博客网络用户绝不会错过重要更新绝不会错过重要更新,在它们成为公众知识之前获得安全警告,并接收有关特别优惠和令人敬畏的新功能的通知。新的新版本可用新网站已安装较新的免费版本(%s)安装了较新的版本(%s)新闻稿下一个不否 - 仅将此站点的数据移动到 %s无 ID%s没有承诺-随时取消%s天没有承诺-随时取消!不需要信用卡永不过期非到期%s的任何计划都不支持试用期。好许可证过期后,您仍然可以使用免费版本,但您将无法使用%s功能。许可证过期后,您将无法再使用%s,除非您使用有效的高级许可证再次激活。选择加入选择退出选择接收有关安全和功能更新、教育内容和临时优惠的电子邮件通知,并共享一些基本的 WordPress 环境信息。选择接收有关安全和功能更新、教育内容和临时优惠的电子邮件通知,并共享一些基本的 WordPress 环境信息。这将帮助我们使 %s 与您的网站更加兼容,并更好地完成您需要它做的事情。选择加入以使“%s”更好!其他所有者电子邮件所有者 ID所有者名称PCI兼容付费附加组件必须部署到Freemius。点击这里许可证密钥安装说明产品的支持电子邮件地址PayPal 帐户电子邮件地址付款付款以美元为单位,每月通过 PayPal 处理。计划计划%s不存在,因此无法启动试用。计划%s不支持试用期。计划ID请在这里联系我们请通过以下信息与我们联系:请下载%s。请输入购买后在电子邮件中收到的许可证密钥:请输入许可证密钥以启用调试模式:请随时提供任何相关的网站或社交媒体统计数据,例如每月独特的网站访问量、电子邮件订户数量、关注者数量等(我们将对这些信息保密)。请按照以下步骤完成升级如果您希望我们与您联系以获取安全和功能更新、教育内容和偶尔的优惠,请告知我们:请注意,取消后,我们将无法获取更新/新订阅的过期定价。如果您选择在未来手动续订,在价格上涨后,将向您收取更新后的价格。请提供有关您打算如何推广%s的详细信息(请尽可能具体)。请提供您的全名。插件插件主页插件ID插件安装变更日志说明常见问题解答功能和定价安装其他注意事项评论插件是一个“服务软件”,这意味着它没有高级代码版本。插件插件和主题同步高级版本高级%s版本已成功激活。已安装高级加载项版本。高级版高级版本已激活。定价隐私政策继续进程 ID处理产品计划摘要推广方式省公钥购买许可证购买更多快速反馈配额重新发送激活电子邮件向新客户介绍我们的%s,并在每次销售成功时获得%s佣金!更新许可证立即续订许可证请求需要 PHP 版本需要WordPress版本复位失活休眠结果软件开发工具包SDK 路径保存%s已保存计划的 Crons屏幕截图按网址搜索密钥从外部域运行的安全HTTPS%s页面似乎关于您的订阅取消发生了一些暂时的问题。请过几分钟再试一次。看来关于您的试用取消发生了一些暂时的问题。请过几分钟再试一次。好像你得到了最新的版本。选择国家发送许可证密钥设置数据库选项与 %s 共享诊断数据有助于提供与您的网站更相关的功能,避免可能破坏您的网站的 WordPress 或 PHP 版本不兼容,并识别插件应该翻译和定制的语言和地区。显示错误详细信息模拟试用推广模拟试用推广单站点许可证站点 ID站点已成功选择加入。站点跳过&%sSlug暂停 & %s因此,当%s不再活动时,您可以重新使用许可证。社交媒体(脸书、推特等)很抱歉给您带来不便,如果您能给我们一个机会,我们将竭诚为您服务。对不起,我们无法完成电子邮件更新。具有相同电子邮件的另一个用户已注册。起点开始调试开始试用开始我的免费%s国家保持联系停止调试提交提交 & %s订阅支持支持论坛从服务器得到的同步数据税务/增值税ID服务条款感谢您申请我们的推广联盟计划,不幸的是,我们现在决定拒绝您的申请。请在30天后再试。感谢您申请我们的推广联盟计划,我们将在未来14天内审查您的详细信息,并将与您联系提供更多的信息。感谢您更新到 %1$s v%2$s!非常感谢您使用%s及其附加组件!非常感谢您使用%s!非常感谢您使用我们的产品!谢谢您!谢谢%s!感谢您确认所有权变更。刚刚向%s发送了一封电子邮件以获得最终批准。谢谢你的升级。谢谢!%1$s 将定期向 %2$s 发送必要的许可数据以检查安全和功能更新,并验证您的许可的有效性。%s破坏了我的网站%s不起作用%s没有按预期工作%s很好,但我需要您不支持的特定功能%s不起作用%s突然停止工作以下产品安装过程已开始,可能需要几分钟才能完成。请等待完成-不要刷新此页。以下产品已进入安全模式,因为我们注意到 %3$s 是 %1$s 的精确副本:%2$s以下产品已进入安全模式,因为我们注意到 %3$s 是这些网站的精确副本:%1$s%2$s远程插件包不包含具有所需段塞的文件夹,重命名失败。%s的升级已成功完成。主题主题切换主题有一个%s的%s可用。有新版本的%s可用。处理您的请求时出现意外的 API 错误。请过几分钟再试,如果还是不行,请联系 %s 的作者并提供以下信息:此插件尚未标记为与您的WordPress版本兼容。此插件尚未用当前版本的WordPress进行测试。此插件需要较新版本的 PHP。这将允许 %s时间戳标题为避免因 WordPress 或 PHP 版本不兼容而破坏您的网站,并识别 %s 应翻译和定制的语言和地区。确保兼容性并避免与您安装的插件和主题发生冲突。要进入调试模式,请输入许可证所有者的密钥(用户ID=%d),您可以在用户仪表板的“我的配置文件”部分找到该密钥:使您能够管理和控制许可证的激活位置,并确保%s安全和功能更新仅发送到您授权的网站。要提供与您的网站相关的其他功能,请避免可能破坏您的网站的 WordPress 或 PHP 版本不兼容,并识别 %s 应翻译和定制的语言和地区。总计城镇审判类型无法连接到文件系统。请确认您的凭据。无限许可证无限更新佣金无上限。最多%s个站点更新更新许可证更新、公告、营销,无垃圾邮件升级上传并激活下载的版本W00t用户仪表盘用户 ID用户密钥用户值验证邮件刚刚发送到%s。如果5分钟后找不到,请检查垃圾邮件框。已验证的验证电子邮件版本%s已发布。查看%s状态查看基本 %s 信息查看基本资料信息查看基本网站信息查看诊断信息查看许可证要点查看插件和主题列表查看详细信息查看付费功能警告我们看不到与该电子邮件地址关联的任何活动许可证,您确定它是正确的地址吗?我们在系统中找不到您的电子邮件地址,您确定地址正确吗?无法加载加载项列表。这可能是我们这边的问题,请几分钟后再来。我们引入了此选项,这样您就不会错过重要的更新,并帮助我们使 %s 与您的站点更加兼容,并更好地完成您需要它做的事情。我们对%s,%s进行了一些调整我们很高兴介绍Freemius网络级集成。网站、电子邮件和社交媒体统计(可选)欢迎使用%s!首先,请输入您的许可证密钥:你期望什么?什么功能?你的%s是什么?你愿意付出什么价钱?你在找什么?%s的名字是什么?您打算在哪里推广%s?WordPress 和 PHP 版本、网站语言和标题WordPress.org 插件页面要将 %s 合并到 %s 中吗?是否继续更新?是是-%s是的 - 两个地址都是我的是 - 将我的所有数据和资产从 %s 移至 %s是的,%%2$s 正在替换 %%4$s。我想将我的 %s 从 %%4$s 迁移到 %%2$s。是的,%2$s 是 %4$s 的副本,用于测试、暂存或开发。是的,%2$s 是一个与 %4$s 不同的新网站。您以前已经试用过。您只需单击一次就可以开始%1$s天的%2$s计划免费试用。你们都很好!您已经在试用模式下运行%s。你离%s只差一步。您仍然可以享受所有%s功能,但您将无法获得%s安全和功能更新以及支持服务。您没有访问高级版本的有效许可证。您有%s许可证。您购买了 %s 许可证。您已成功更新%s。您将此网站 %s 标记为 %s 的临时副本。您将此网站 %s 标记为这些网站的临时副本您可能错过了,但您不必共享任何数据,只需选择%s即可。在接下来的 5 分钟内,您将在 %2$s 的邮箱中收到 %3$s 的 %1$s。您应该会在 %2$s 的邮箱中收到一封 %1$s 的确认邮件。请确保您点击了发送至 %3$s 的电子邮件中的按钮。您应该会在 %s 的邮箱中收到一封 %s 的确认电子邮件。请确保单击该电子邮件中发送给 %s 的按钮。您已经选择加入我们的使用情况跟踪,这有助于我们不断改进%s。您已经选择了我们的使用情况跟踪,这有助于我们不断改进它们。%s加载项计划已成功升级。您的%s免费试用已成功取消。您的%s许可证被标记为白标,以对WP管理员隐藏敏感信息(例如,您的电子邮件,许可证密钥,价格,账单地址和发票)。如果您希望将其还原,则可以通过%s轻松完成。如果这是一个错误,您也可以%s。您的%s许可证已成功停用。您的 WordPress 用户:名字和姓氏,以及电子邮件地址您的帐户已用%s计划成功激活。您的%s会员申请已被接受!登录到您的分支区域,地址为%s。您的会员帐户已暂时挂起。你的电子邮件已被成功验证-你太棒了!您的免费试用已过期。%1$s立即升级%2$s以继续使用%3$s而不中断。您的免费试用已过期。您仍然可以继续使用我们所有的免费功能。你的许可证被取消了。如果您认为这是一个错误,请联系支持人员。您的许可证过期了。%1$s立即升级%2$s以继续使用%3$s而不中断。您的许可证过期了。您仍然可以继续使用所有%s功能,但您需要续订许可证才能继续获取更新和支持。您的许可证过期了。您仍然可以继续永远使用免费的%s。您的许可证已成功激活。您的许可证已成功停用,您回到了%s计划。您的姓名已成功更新。您的计划已成功激活。您的计划已成功更改为%s。您的计划已成功升级。您的服务器阻止访问 Freemius 的 API,这对 %1$s 同步至关重要。请联系您的主机以将以下域列入白名单:%2$s您的订阅已成功取消。您的%s计划许可证将在%s后过期。您的试用已成功开始。邮政编码就在在此处激活许可证活跃的没有%s,%s无法运行。没有插件,%s无法运行。抬头允许剩下%s正在激活年API关闭调试恭喜此路不通已连接下载最新版本下载最新免费版本每月到期路径发送电子邮件瞬间每年每年一次计划没有秘密SDK 版本许可证同步同步许可证作者关闭打开基于%s开始免费试用关闭关闭完成选择加入数据天停用中代表不%s要%s向我发送安全和功能更新、教育内容和服务。%s计划帐单%s最好的嘿哎呀嘿%s,小时小时安装哟哈许可证站点毫秒新测试版新版本未验证价格定价可选的版本产品现在还原秒好像你输入的钥匙和我们的记录不符。请给我发送安全和功能更新,教育内容和优惠。跳过嗯开始试用订阅切换中上述网站这儿有最新的%s版本试用试用删除降级编辑隐藏选择加入选择退出购买显示跳过更新升级%s以前freemius/languages/freemius-ta.mo000064400000220731147600046700013120 0ustar00!!A!n!h^"S"%# A# M#Y#`#6s##__$#$$ $ % %%&%.%7%?%@H%H%%O%5&9&X&x&&&& &&&&&&&-!'O' d'n'}''''5''' ( ('(@( Y( f(p(o(((G)))**"**2+!J+al+0++,(,7,?,S,X,`,s,|,, , ,, ,,,,, --- - - ---Y.k.z. .....(.1.%)/:O///// / /// // /0x00 )1 61@1X1l1tt1122/282L2k2 22L3[3f@44 44Y4a5{555 5 5;566 6 7 7 7 )767jE77 77372898~M8U8"9>9W9)r9-99S92:'J:r:Mu:7:g:pc;;;y;n<< <<<< <= =1"=.T=O==AS>y>?/?aE??B?,?@ @ -@:@X@ q@|@@@ @ @@@4@ A A"A&A-A5A:[y[&[Z[T\Rg\.\.\9]ZR]3]<]b^P^U^_(__K#`(o`G`#`%a)*a$TaUya)aa b(b;=b6yb>bbbc0c$Fckcccc&c*d7-ded|dd3ddde*e>e*[e1eee#e f(f:f JfVfvff ffNfg&gDg_goggg1gggg h h 'h 4h ?hLh dhCphhQh i i (i2iMiSi firi i i i i i i i i i iindo5dpNqKq'5r]r|r8rr4lstK^uHu+u*v3Jv~v$v-vvvxw|zw:w2xxxxx.y?y$Xy'}y:y:yVz4rzz3{5{9|-N|+||A|||}}} ~'"~J~h~=B11 yt7|A*jl6׆KZF7`:Ӊ"  *00[-(؊A"C.f7݋)c72ōA:!S$u.Ɏ"%ȏ% 'AZp$4#X+n7'ҒJ ER j'w=)ݓX'` VP:V"hika@_M!3oe1/atg ~$37(kY z@4ڦ -KJ!Ψרy>$Y|~YܭfgS>cV İy_޳>GP\{)@EJ Ѷ</ KUuLºVdNZ e&*4"տxYq( 7 C5OA PZ^ex. ;A Q^vGqRF*;R'*y[P_%dC=V!. Q1|rf";.d|o@RORFk4  &$*O+k$!K,m;1qp= $='Xe( 4('1P{F4F{'A,?C$L'q", ma@$E jx#H4Jh>m7MCYw  .\~GyGX7lr1WIA7@yI7 ".g^*" )!6Xk"F&?m"!1$!S1XY6U:G=lm&9Zc#~k`&os9 D u1PC3w~JMR&w6  S 4 { F H P`HOpv  S.$S'\7M !&3H|.'* *5D`c % ;OE' "6I N'[0u1QBq4%;?^'x8% E3 y@   ; !X !z      %!=!S!f!!!!!! %s to access version %s security & feature updates, and support. The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is the new owner of the account.%s minimum payout amount.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s to access version %s security & feature updates, and support.%s tracking cookie after the first visit to maximize earnings potential.%s's paid features%sClick here%s to choose the sites where you'd like to activate the license on.APIASCII arrow left icon←ASCII arrow right icon➤AccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.An unknown error has occurred while trying to set the user's beta mode.An unknown error has occurred.An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre you sure you want to delete all Freemius data?Are you sure you want to proceed?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBetaBillingBilling & InvoicesBlockingBlog IDBodyBundle PlanBusiness nameBuy a license nowBuy licenseCan't find your license key?CancelCancel %s & ProceedCancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site.Cancel %s?Cancel InstallationCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanCheckoutCityClear API CacheClear Updates TransientsClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site.Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDelegate to Site AdminsDelete All AccountsDetailsDon't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support.Don't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload the latest %s versionDownload the latest versionDownloadedDue to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.During the update process we detected %d site(s) that are still pending license activation.During the update process we detected %s site(s) in the network that are still pending your attention.EmailEmail addressEndEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used for the upgrade below and we will resend you the license key.ErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.FreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFull nameFunctionGet commission for automated subscription renewals.Get updates for bleeding edge Beta versions of %s.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf you click it, this decision will be delegated to the sites administrators.If you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.Important Upgrade Notice:In %sIn case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?Install Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid module ID.Invalid site details collection.InvoiceIs ActiveIt looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's not what I was looking forJoin the Beta programJust letting you know that the add-ons information of %s is being pulled from an external server.KeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense KeyLicense keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerMessageMethodMigrate Options to NetworkMobile appsModuleModule PathModule TypeMore information about %sNameNetwork BlogNetwork UserNewNew Version AvailableNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license.Opt InOpt OutOpt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.PayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.Please provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProceedProcess IDProcessingProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicensePurchase MoreQuick FeedbackQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRenew your license nowRequestsRequires WordPress VersionResultSDKSDK PathSave %sScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes.Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSimulate Network UpgradeSimulate Trial PromotionSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSocial media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart TrialStart my free %sStateSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a %s of %s available.There is a new version of %s available.This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.TimestampTitleTotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser IDUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress.org Plugin PageWould you like to proceed with the update?YesYes - %sYou already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.You do not have a valid license to access the premium version.You have a %s license.You have successfully updated your %s.You might have missed it, but you don't have to share any data and can just %s the opt-in.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully activated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your subscription was successfully cancelled. Your %s plan license will expire in %s.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onactive add-onActiveaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,installed add-onInstalledinterjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew Beta versionnew versionnot verifiednounPricenounPricingproduct versionVersionsecondssecsend me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Sankar Srinivasan , 2019 Language-Team: Tamil (http://app.transifex.com/freemius/wordpress-sdk/language/ta/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: ta Plural-Forms: nplurals=2; plural=(n != 1); X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 %s பதிப்பின் பாதுகாப்பு & வசதி மேம்படுத்தல் மற்றும் உதவிக்கு%s.%1$sன் விலையுள்ள பதிப்பு ஏற்கனவே நிறுவப்பட்டுள்ளது. %2$s வசதிகளை பயன்படுத்த அதை செயல்படுத்தவும். %3$sவருகின்ற அனைத்து பணம் செலுத்துதல்களையும் %1$s உடன் நிறுத்துகிறது, மற்றும் உங்கள் %2$s திட்ட உரிமம் %3$sல் காலாவதியாகிறது."%s" செயல்படுத்தலை முடியுங்கள்%s ஆட்-ஆனை வாங்கிவிட்டீர்கள்.%s நிறுவுதல்கள்%s உரிமங்கள்%s முன்பு%sம் அதன் ஆட்-ஆன்களும்ஒரு வாடிக்கையாளர் புது உரிமம் வாங்கும்போது %s லாபப் பங்கீடு.ஆட் ஆன் விலையுள்ளது என்பதால் %sன் விலையில்லா முன்னோட்டம் ரத்தானது. நீங்கள் உரிமம் வாங்கிப் பயன்பாட்டைத் தொடரலாம்.%s ஒரு விலையுள்ள ஆட்ஆன். ப்ளக்இன் செயல்பட நீங்கள் உரிமம் வாங்கவேண்டும்கணக்கின் புதிய உரிமையாளர் %s.%s குறைந்தபட்ச பணம் பெறுதல்.%s அல்லது அதிகமாக%s மதிப்பீட்டெண்%s மதிப்பீட்டெண்கள்%s sec%s நட்சத்திரம்%s நட்சத்திரங்கள்%s முறை%s முறைகள்%s பாதுகாப்பு மேம்படுத்தல் மற்றும் உதவிக்கு %sவருமானம் அதிகரிக்க, முதல் தள வருகையில் %s tracking cookie.%sன் விலையுள்ள வசதிகள்உங்களுக்கு வேண்டிய தளங்களில் உரிமத்தை செயல்படுத்த %s கிளிக் செய்க %sAPI←➤கணக்குகணக்கு விபரங்கள்செயல்கள்செயல்படுத்து%s செயல்படுத்து%s திட்டம் செயல்படுத்த%s வசதிகளை செயல்படுத்தவிலையில்லா பதிப்பை செயல்படுத்தஉரிமம் செயல்படுத்தமீதமுள்ள எல்லா தளங்களிலும் உரிமத்தை செயல்படுத்துகவலைப்பின்னலில் உள்ள எல்லா தளங்களிலும் உரிமத்தை செயல்படுத்துகஆட்-ஆன் செயல்படுத்தசெயல்படுத்தப்பட்டது%sக்கான ஆட்ஆன்கள்Module %sன் ஆட்ஆன்கள்அடுத்த தளத்தைச் சேர்க்கஆட் ஆன்ஆட்-ஆன்ஸ்ஆட்ஆன் WordPress.org அல்லது Freemius ஏதாவது ஒன்றில் வரிசைப்படுத்தப் பட்டிருக்க வேண்டும்.முகவரிமுகவரி வரி %dAffiliateபுரிந்துணர்வுவிலையில்லா %sக்குப் பிறகு, குறைந்தளவு %s செலுத்துங்கள்ஒப்புக்கொண்டு உரிமத்தை செயல்படுத்துகஅனைத்து வேண்டுகோள்கள்அனைத்து மாதிரிகள்அனுமதித்து தொடர்கஇல்லாவிட்டால், Network-level கணக்குப் பக்கத்தில் எப்போது வேண்டுமானாலும் உரிமத்தை செயல்படுத்திக் கொள்ளலாம்.தொகை%sலிருந்து %s (பணம் செலுத்திய) பதிப்பின் தானியங்கி பதிவிறக்கமும், நிறுவுதலும் %sல் ஆரம்பமாகும். இதை நீங்களே செய்ய விரும்பினால் ரத்து செய்யும் பட்டனை அழுத்தவும். உபயோகிப்பாளரின் பீட்டாவை செயல்படுத்துகையில், புதிய தவறு உருவாகியுள்ளது.என்னதென்றே தெரியாத ஒரு தவறு நேர்ந்துவிட்டதுஎச்சரிக்கை: %sன் முன்னோட்ட Beta பதிப்பின் மேம்பட்ட வடிவம் பழைய பதிப்பின் மீது நிறுவப்படுகிறது. அனாமதேய பின்னூட்டம்மீதமுள்ள அனைத்து தளங்களின் மீதும் பிரயோகிக்கவலைப்பின்னலின் அனைத்து தளங்களின் மீதும் பிரயோகிக்கAffiliate ஆக விண்ணப்பியுங்கள்அனைத்து Freemius தகவலையும் அழிக்க விருப்பமா?மேலே தொடர விருப்பமா?வாடிக்கையாளர் 30 நாட்களுக்கு பணம் திரும்பப் பெறலாம் Refund என்பதால் உங்களுக்கு லாபப் பங்கீடு 30 நாட்களுக்குப் பிறகே கிடைக்கும்.முன்னரே தெரிவு செய்திருந்தால் மட்டுமே தானியங்கி நிறுவுதல் நடைபெறும்.%sல் தானாக புதுப்பிக்கிறதுதானியங்கி நிறுவுதல்உத்தேச மதிப்பீட்டெண்அடி தூள்Affiliate ஆகுங்கள்பீட்டாவசூல்பில் & இன்வாய்ஸ்தடுக்கப்படுகிறதுBlog IDஅமைப்புகூட்டுத்திட்டம்தொழிலின் பெயர்புது உரிமம் வாங்குங்கள்உரிமம் வாங்கLicense Key காணவில்லையா?ரத்து%s ரத்து செய்க & தொடர்கநான் %sஐ இந்த தளத்திலோ அல்லது பிற தளத்திலோ உபயோகிக்க விரும்பவில்லை என்பதால் %sக்கு உதவியோ, பாதுகாப்பு மேம்படுத்தலோ தேவையில்லை. %sஐ ரத்து செய்யவும்.%s ரத்து செய்யவா?நிறுவுதலை ரத்துசெய்சந்தாவை ரத்து செய்வெள்ளோட்டம் ரத்து செய்கரத்தானது%s ரத்தாகிறது%s ரத்தாகிறது...சந்தா ரத்தாகிறதுவெள்ளோட்டத்தை ரத்து செய்தால் அனைத்து விலையுள்ள வசதிகளும் நிறுத்தப்படும். சரியா?உரிமம் மாற்றஉரிமை மாற்றம்திட்டம் மாற்றCheckoutஊர்API Cache நீக்கClear Updates Transientsபிளக்இன்னை அநாமதேயமாக பயன்படுத்த இங்கே கிளிக் செய்யவும்%s குறித்த மதிப்பீடு & விமர்சனங்களை கிளிக் செய்து காண்ககிளிக் செய்து %dன் முழுஅளவு திரைநகல் காண்கதயாரிப்புகள்Codeஒத்திசைவு உயர்நிலைதொடர்புஉதவியை அணுகவும்தொடர்பு கொள்ளுங்கள்பங்கெடுத்தோர்%sஐ செயல்படுத்த முடியவில்லை.நாடுCron மாதிரிதேதிசெயல்நிறுத்துஉரிமத்தை செயல்நிறுத்த%sஐ செயல்நிறுத்தினாலோ, அகற்றினாலோ பிற தளங்களில் பயன்படுத்தும் வாய்ப்பிருந்தும், உரிமம் தானாகவே செயலிழக்கும்.உரிமத்தை செயல்நிறுத்துவதானது, அனைத்து விலையுள்ள வசதிகளையும் நிறுத்திவிடும். ஆனாலும் பிற தளங்களில் செயல்படுத்தலாம். தொடரலாமா?செயல்நிறுத்துDebug Logதள நிர்வாகிகளுக்கான சிறப்பாளர்அனைத்து கணக்குகளையும் அழிக்கவிபரங்கள்%s ரத்துசெய்ய வேண்டாம் - அதற்கு உதவியை அணுகவும், பாதுகாப்பு மேம்படுத்தல்களைப் பெறவும் விரும்புகிறேன்.License Key இல்லையா?இந்த பிளக்இன்னுக்கு நன்கொடை தாருங்கள்உங்கள் திட்டம் கீழ்ப்படுத்தப்படுகிறதுபதிவிறக்கு%s பதிப்பை பதிவிறக்கலாம்%sன் அண்மைய பதிப்பைப் பதிவிறக்கலாம்புதிய பதிப்பை பதிவிறக்கலாம்பதிவிறக்கப்பட்டதுபுதிய %s EU GDPRன் படி %s மேற்பார்வை விதிகளால், ஆட்சேபகர தகவலுக்கு எதிரான உங்கள் நிலையை உறுதி செய்கிறீர்கள் :)எங்கள் affiliate திட்ட விதிமுறை மீறல் காரணமாக உங்கள் affiliate கணக்கை தற்காலிகமாக தடை செய்கிறோம். கேள்விகள் இருந்தால் உதவியை தொடர்பு கொள்ளவும்.மேம்படுத்தல் நடைபெறும்போதே இன்னும் %d தளங்களில் உரிமம் செயல்பாட்டில் இல்லை என்று அறிகிறோம்.மேம்படுத்தல் நடைபெறும்போதே Networkல்லுள்ள %s தளங்கள் உங்கள் கவனிப்பைக் கோருகின்றன என்றறிகிறோம்.மின்னஞ்சல்மின்னஞ்சல் முகவரிமுடிவு%sஐ எந்தெந்த தளங்களில் முன்னிலைப் படுத்துவீர்களோ அந்தத் தளங்களின் பெயர்களை உள்ளிடுங்கள்.தரம் உயர்த்துதலின் போது நீங்கள் உள்ளிட்ட மின்னஞ்சல் முகவரியைத் தந்தால், license keyஐ மீண்டும் அனுப்புகிறோம்.தவறுசெர்வரிடம் இருந்து தவறுச் செய்தி வந்திருக்கிறது.காலாவதியானது%sல் காலாவதியாகிறதுமேலதிக தளங்கள்தயாரிப்புகளை சந்தைப்படுத்தும் மேலதிக தளங்கள்.FileFilterWordpress.orgயின் வழிகாட்டு நெறிமுறைகள்படி, உங்கள் வெள்ளோட்டம் துவங்கும்முன் நாங்கள் கேட்டுக் கொள்வதெல்லாம், உங்கள் பயன்பாட்டுத் தகவலை நாங்கள் பின்தொடர எங்களை அனுமதிக்கும் தெரிவை தெரிவு செய்யுங்கள் என்பதே. இது %sஐ அனுமதித்து தகவலை %sக்கு அனுப்பச்செய்து மேம்படுத்தலுக்கு உதவும். மற்றும் உங்கள் வெள்ளோட்டத்தை உறுதிசெய்யும்.விலையில்லைவிலையில்லா வெள்ளோட்டம்விலையில்லா பதிப்புFreemius APIFreemius தவறுநீக்கி Debugப்ளக்இன் முக்கிய கோப்பை Freemius SDKவால் கண்டறிய முடியவில்லை. தயவுசெய்து பின்வரும் செய்தியுடன் sdk@freemius.comக்கு மின்னஞ்சல் அனுப்பவும்Freemius நிலைமுழுப்பெயர்Functionதானியங்கி சந்தா புதுப்பித்தலுக்கும் லாபப் பங்கீடு பெறுங்கள்.%sன் பீட்டா பதிப்பு மேம்படுத்தலைப் பெறுங்கள்.License key உள்ளதா?வணக்கம். %sன் முகவர் திட்டம் குறித்து உங்களுக்குத் தெரியுமா? %sஐ நீங்கள் விரும்பினால், நீங்களும் முகவராகி பணம் ஈட்டலாம்!%sஐ எந்தளவு விரும்புகிறீர்கள்? %d-நாள் விலையில்லா வெள்ளோட்டத்தில் %sன் விலையுள்ள வசதிகளை சோதித்துப் பாருங்கள்.பதிவேற்றுதல் மற்றும் செயல்படுத்துதல் எப்படி?எங்களை நீங்கள் எப்படி முன்னிலைப் படுத்துவீர்கள்?இதற்குமேல் பணம் செலுத்தமாட்டேன்இதை எப்படி உபயோகிப்பது என்று எனக்குப் புரியவில்லைஎன் தனிப்பட்ட தகவலை உங்களோடு பகிர விரும்பவில்லை.எனக்கு வேறு ஒரு நல்ல %s கிடைத்துவிட்டதுஎன் திட்டத்தை மேம்படுத்திய பின்னும், என் உரிமம் %s என்பதாகவே காட்டுகிறது.இனி எனக்கு %s தேவையில்லைகுறுகிய காலத்திற்கு மட்டும் %s போதும்ஐடிஇதை கிளிக் செய்தால், இந்த முடிவு தள நிர்வாகிகளுக்கு அனுப்பப்படும்.ஏன் நீங்கள் %s என்பதை எங்களுக்குத் தெரிவியுங்கள்உங்கள் %s கணக்கின் உரிமையை %sக்கு மாற்றிட விரும்பினால் உரிமை மாற்றம் பட்டனை அழுத்தவும்.அந்தத் தளங்களிலும் %sஐ உபயோகிக்க விரும்பினால், கீழே License Key உள்ளிட்டு செயல்படுத்தலை அழுத்தவும்.முக்கியமான மேம்படுத்தல் அறிவிப்பு%sல்ஒருவேளை %s இந்த தளத்திலோ அல்லது பிற தளத்திலோ உபயோகிக்கவில்லை என்றால் %sஐ ரத்து செய்ய விரும்புகிறீர்களா?விலையில்லா பதிப்பை நிறுவலாம்விலையில்லா பதிப்பின் மேம்படுத்தலை நிறுவலாம்நிறுவலாம்மேம்படுத்தலை நிறுவலாம்%s: பிளக்இன் நிறுவப்படுகிறதுmodule ID தவறானது.தவறான தள விவர சேர்ப்புஇன்வாய்ஸ்Is Activeஉங்கள் உரிமம் செயல்பாட்டுக்கு வரவில்லையென தோன்றுகிறது.உரிமத்தின் செயல்நிறுத்தம் தோல்வி அடைந்ததுபோல் தெரிகிறது.வெள்ளோட்டத்தில் நீங்கள் இல்லை என்பதால், அதை ரத்துசெய்யத் தேவையில்லை :)நீங்கள் இன்னும் %s திட்டத்திலேயே இருப்பதாகத் தெரிகிறது. நீங்கள் திட்டத்தை மாற்றிய பின்னர் இப்படி இருந்தால், அது எங்கள் தவறு. மன்னிக்கவும். உங்கள் தளத்திற்கு உரிமம் ஏதும் இல்லை என்பது போல் தெரிகிறது.சரிபார்க்கும் வகையினங்களில் ஏதோ ஒன்று தவறுபோல் தெரிகிறது. உங்கள் Public Key, Secret Key & User ID ஆகியவற்றை சரிபார்த்து மீண்டும் முயற்சிக்கவும்.நான் எதிர்பார்த்தது இதுவல்ல.பீட்டா பதிப்பு சோதனையில் சேரவும்%sன் ஆட்-ஆன் தகவலை வெளியிலுள்ள சர்வர் மூலம் எடுக்கிறோம் என்பதை அறியவும்.Keyஎன்ன வேலை செய்யவில்லை என்பதை விளக்கமாக சொன்னால், அதை நாங்கள் சரி செய்வோம்.காரணம் எதுவென்று சொன்னால் எங்களை மேம்படுத்திக் கொள்வோம்.கடைசிகடைசி மேம்படுத்தல்கடைசி உரிமம்சமீபத்திய விலையில்லா பதிப்பு நிறுவப்பட்டதுசமீபத்திய பதிப்பு நிறுவப்பட்டதுமேலும் அறியநீளம்உரிமம்உரிம ஒப்பந்தம்License KeyLicense keyLicense key காலியாக உள்ளது.வாழ்நாள்%sஐ விரும்புகிறீர்களா? எங்கள் Ambassador ஆக பணியாற்றி பணம் பெறலாம் :-)Load DB OptionLocalhostLogLoggerசெய்திவழிமுறைMigrate Options to Networkஅலைபேசி செயலிகள்ModuleModule PathModule மாதிரி%s குறித்த மேலதிக தகவல்பெயர்Network BlogNetwork பயனர்புதியதுபுதிய பதிப்பு கிடைக்கிறதுபுதிய விலையில்லா பதிப்பு (%s) நிறுவப்பட்டதுபுதிய பதிப்பு (%s) நிறுவப்பட்டதுசெய்திக்கடிதம்அடுத்துஇல்லைஐடி இல்லை%sக்கு எந்தக் கடப்பாடும் இல்லை - எப்போதும் ரத்து செய்யலாம்!%s நாட்களுக்கு எந்தக் கடப்பாடும் இல்லை - எப்போதும் ரத்து செய்யலாம்!கடன் அட்டை தேவையில்லைகாலாவதியாகாதுகாலாவதியாகாதது%sன் எந்தத் திட்டங்களிலும் வெள்ளோட்டம் இல்லை.O.Kஉங்கள் உரிமம் முடிந்ததும் நீங்கள் அனைத்து விலையில்லா வசதிகளையும் பயன்படுத்திக் கொள்ள முடியும். ஆனால் %s வசதிகளை அணுக இயலாது.மீண்டும் உரிமத்தை செயல்படுத்தினால் தவிர, உரிமம் காலாவதியானால் %s பயன்படுத்த முடியாது.தெரிவு செய்தெரிவை அகற்று"%s"ஐ சிறப்பானதாக்க தேர்வு செய்யுங்கள்மற்றவைஉரிமையாளர் மின்னஞ்சல்உரிமையாளர் IDஉரிமையாளர் பெயர்PCI compliantவிலையுள்ள ஆட்ஆன் Freemiusல் வரிசைப்படுத்தப் பட்டிருக்க வேண்டும்.PayPal கணக்கின் மின்னஞ்சல் முகவரிபணம் செலுத்தல்கள்பணம் பெறுதல் USDயில் மாதாமாதம் PayPal மூலம் பெறலாம்.திட்டம்%s திட்டம் இல்லை. வெள்ளோட்டம் துவங்க இயலாது.%s திட்டத்திற்கு வெள்ளோட்டம் கிடையாது.திட்ட IDஎங்களை இங்கு அணுகலாம்பின்வரும் செய்தியோடு எங்களைத் தொடர்பு கொள்ளுங்கள்%sஐ பதிவிறக்கலாம்.உங்கள் மின்னஞ்சலுக்கு வந்த License Keyஐ உள்ளிடுங்கள்:இணைய தள அல்லது சமூக ஊடக வருகையாளர் எண்ணிக்கை, மின்னஞ்சல் சந்தாதாரர்கள், பின்தொடர்வோர் போன்ற புள்ளிவிவரங்கள் தருக. (நாங்கள் ரகசியம் காப்போம்)மேம்படுத்தலை முடித்துவைக்க பின்வரும் வழிமுறையைப் பின்பற்றவும்பாதுகாப்பு & மேம்படுத்தல், விளக்கவுரை மற்றும் தள்ளுபடி விவரங்களை உங்களுக்கு நாங்கள் அனுப்ப விரும்பினால் எங்களைத் தொடர்பு கொள்ளுங்கள்.தயவுசெய்து கவனிக்கவும். ரத்துசெய்த பிறகு மீண்டும் புதிய சந்தா/புதுப்பித்தலுக்கு பழைய விலையை எங்களால் வசூலிக்க முடியாது. விலை ஆண்டுக்கொரு முறை உயரும். நீங்கள் இனி புதுப்பிக்க விரும்பினால் புதிய விலையை செலுத்தவேண்டும்.%sஐ எப்படி முன்னிலைப் படுத்துவீர்கள் என்ற விவரம் தரவும். (தயவுசெய்து குறிப்பிட்டுச் சொல்லவும்)உங்கள் முழுப் பெயரைத் தரவும்.ப்ளக்இன்பிளக்இன் முகப்புப்பக்கம்பிளக்இன் IDபிளக்இன் நிறுவுதல்Changelogவிளக்கம்FAQவசதிகள் & விலைநிறுவுதல்பிற குறிப்புகள்கருத்துரைகள்விலையுள்ள நிரல் இல்லாததால் பிளக்இன் "Serviceware" எனப்படும்.பிளக்இன்கள்பிளக்இன் & தீம் SyncPremium%s விலையுள்ள பதிப்பு வெற்றிகரமாக செயல்பாட்டுக்கு வந்தது.விலையுள்ள ஆட்-ஆன் பதிப்பு ஏற்கனவே நிறுவப்பட்டுள்ளது.விலையுள்ள பதிப்புவிலையுள்ள பதிப்பு ஏற்கனவே செயலில் உள்ளது.விலை விவரம்தனியுரிமைக் கொள்கைகள்தொடர்கProcess IDசெயலில்தயாரிப்புகள்திட்டத்தின் சுருக்கம்முன்னிலைப்படுத்தும் வழிமுறைகள்மாநிலப் பரப்புPublic Keyஉரிமம் வாங்குங்கள்மேலும் வாங்குகஉடனடி பின்னூட்டம்ஒதுக்கீடுசெயல்படுத்தும் மின்னஞ்சலை மீண்டும் அனுப்புகஎங்கள் %sக்கு புதிய வாடிக்கையாளர்களை பரிந்துரை செய்து, ஒவ்வொரு விற்பனைக்கும் %s லாபப் பங்கீடாகப் பெறலாம்.உரிமத்தை புதுப்பியுங்கள்உரிமத்தை புதுப்பியுங்கள்வேண்டுகோள்கள்WordPress பதிப்பை வேண்டுகிறதுமுடிவுSDKSDK Path%s சேமிக்கலாம்பட்டியலிட்ட Cronsதிரை நகல்கள்முகவரி மூலம் தேடSecret Keyபாதுகாப்பான HTTPS %s பக்கம், வெளி முகவரியிலிருந்து இயங்குகிறதுஉங்கள் சந்தா ரத்து செய்வதில் ஒரு தொழில்நுட்பக் கோளாறு. மீண்டும் முயற்சிக்கவும்.94%match உங்கள் வெள்ளோட்டம் ரத்து செய்வதில் ஒரு தொழில்நுட்பக் கோளாறு. மீண்டும் முயற்சிக்கவும்புதிய பதிப்பு உங்களுக்குக் கிடைத்துவிட்டது போல் தெரிகிறது.நாட்டைத் தேர்ந்தெடுக்கLicense key அனுப்புகSet DB OptionSimulate Network UpgradeSimulate Trial Promotionஒரு தள உரிமம்இணையதள ஐடிதளம் தெரிவு செய்யப்பட்டது.தளங்கள்கடந்திடு & %sSlugசமூக ஊடகங்கள் (Facebook, Twitter etc.)தவறுக்கு வருந்துகிறோம். எங்களுக்கு ஒரு வாய்ப்புத் தந்தால் உங்களுக்கு உதவக் காத்திருக்கிறோம்.மன்னிக்கவும்... இன்னொரு பயனாளர் இதே மின்னஞ்சல் முகவரியுடன் ஏற்கனவே பதிவு செய்திருக்கிறார்.துவக்கம்வெள்ளோட்டம் துவக்குஎன் விலையில்லா %sஐ துவக்கவும்மாநிலம்சமர்ப்பி & %sசந்தாஉதவிஉதவி மையம்Sync Data From ServerTax / VAT IDசேவை நிபந்தனைகள்எங்கள் affiliate திட்டத்திற்கு விண்ணப்பித்ததற்காக நன்றி. எதிர்பாரா விதமாக உங்கள் விண்ணப்பம் தள்ளுபடி செய்யப்பட்டது. 30 நாட்களில் மீண்டும் விண்ணப்பிக்கவும்.எங்கள் affiliate திட்டத்திற்கு விண்ணப்பித்ததற்காக நன்றி. உங்கள் விவரங்களை பரிசீலித்து, 14 நாட்களில் மேலதிக தகவலோடு தொடர்பு கொள்கிறோம்.%s மற்றும் அதன் ஆட்-ஆன் பயன்படுத்துவதற்கு நன்றி!%s பயன்படுத்துவதற்கு நன்றி!எங்கள் உருவாக்கங்களைப் பயன்படுத்துவதற்கு நன்றி!நன்றி!நன்றி %s!உரிமை மாற்றத்தை உறுதிப்படுத்தியதற்கு நன்றி. இறுதி ஒப்புதலுக்காக %sக்கு இப்போது ஒரு மின்னஞ்சல் அனுப்பப்பட்டுள்ளது.%s எனது இணையதளத்தை செயலிழக்க வைத்துவிட்டது%s வேலை செய்யவில்லை%s நான் எதிர்பார்த்தது போல் இல்லை%s நல்லதுதான். ஆனால், எனக்கு தேவைப்படும் வசதி இதில் இல்லை%s சரிவர வேலை செய்யவில்லை%s திடீரென நின்றுவிட்டதுநிறுவப்படுகிறது. சில நிமிடங்கள் காத்திருக்கவும். இந்தப் பக்கத்தை Refresh செய்யவேண்டாம்.பெயர் மாற்றமுடியாது. Slug உடனான folder, பிளக்இன் பேக்கில் இல்லை.%sன் மேம்படுத்தல் முடிந்ததுதீம்தீம் மாற்றம்தீம்கள்%sன் %s கிடைக்கிறது.%sன் புதிய பதிப்பு இப்போது கிடைக்கிறது.இந்த பிளக்இன் உங்கள் WordPress பதிப்புடன் ஒத்திசைவானது என்று குறிக்கப்படவில்லை.இந்த பிளக்இன் உங்கள் தற்போதைய WordPress பதிப்புடன் சோதிக்கப்படவில்லை.நேர முத்திரைதலைப்புமொத்தம்நகர்வெள்ளோட்டம்மாதிரிFilesystem அணுக இயலவில்லை. உங்கள் உள்ளீடு சரியா என சோதிக்கவும்.பல்தள உரிமம்அளவில்லா மேம்படுத்தல்கள்அளவில்லா லாபப் பங்கீடு.%s தளங்கள் வரைமேம்படுத்துஉரிமம் மேம்படுத்தமேம்படுத்தல், அறிவிப்புகள், வணிக செய்திகள். Spam இல்லைமேம்படுத்துபதிவிறக்கிய பதிப்பை பதிவேற்றி செயல்படுத்தலாம்W00tஉபயோகிப்பாளர் ஐடிபயனர்கள்Valueஉறுதிப்படுத்தும் மின்னஞ்சல் %sக்கு அனுப்பப்பட்டுள்ளது. பார்க்கவும். 5 நிமிடத்தில் மின்னஞ்சல் வரவில்லை என்றால் Spamல் பார்க்கவும்.உறுதிசெய்யப்பட்டதுமின்னஞ்சல் சரிபார்த்திடுங்கள்%s பதிப்பு வெளியாகிவிட்டது.விபரங்களைப் பாருங்கள்விலையுள்ள வசதிகள் என்னவென்று காணுங்கள்எச்சரிக்கைஇந்த மின்னஞ்சலின் பதிவில் எந்த உரிமமும் இல்லை. தங்கள் மின்னஞ்சல் சரியானதா?உங்கள் மின்னஞ்சல் முகவரியைக் காணவில்லை. நீங்கள் அளித்தது சரியானதா?. %s, %sக்கு சில சுவாரஸ்யங்களை உருவாக்கியிருக்கிறோம்Freemius network-level integrationஐ அறிமுகம் செய்வதில் பேருவகை அடைகிறோம்.இணையதளம், மின்னஞ்சல் மற்றும் சமூக ஊடக புள்ளி விவரங்கள் (விரும்பினால் தரலாம்)நீங்கள் என்ன எதிர்பார்த்தீர்கள்?என்ன வசதி?உங்கள் %s என்ன?என்ன விலை உங்களுக்கு வசதியாக இருக்கும்?நீங்கள் என்ன எதிர்பார்க்கிறீர்கள்?%sன் பெயர் என்ன?%sஐ எங்கு எப்படி முன்னிலைப் படுத்துவீர்கள்?WordPress.org பிளக்இன் பக்கம்மேம்படுத்தப்பட்ட பதிப்பில் தொடர விரும்புகிறீர்களா?ஆம்ஆம் - %sநீங்கள் ஏற்கனவே வெள்ளோட்டம் பார்த்துவிட்டீர்களே.%2$s திட்டத்தின் %1$s-நாள் விலையில்லா வெள்ளோட்டத்தைத் துவக்க இன்னும் 1 கிளிக் மட்டுமே.நல்லது... மகிழ்ச்சிநீங்கள் %sஐ வெள்ளோட்ட நிலையில் உபயோகித்துக் கொண்டிருக்கிறீர்கள்.இன்னும் ஒருபடி அருகில் - %sநீங்கள் %sன் வசதிகளை பயன்படுத்த முடியும். ஆனால் %s பாதுகாப்பு & மேம்படுத்தல் மற்றும் உதவியை அணுக இயலாது.விலையுள்ள பதிப்பை அணுக உங்களிடம் உரிமம் இல்லை.உங்களிடம் %sன் உரிமம் உள்ளதுஉங்கள் %s மேம்படுத்தப்பட்டது.விட்டுவிட்டீர்கள், ஆனாலும் நீங்கள் எந்த தகவலையும் பகிர வேண்டியதில்லை %sதெரிந்தெடுப்பு மட்டுமேஉங்கள் பயன்பாட்டைப் பின்தொடர எங்களுக்கு நீங்கள் அளித்த அனுமதியானது, %sஐ மேம்படுத்த உதவும்.உங்கள் பயன்பாட்டைப் பின்தொடர எங்களுக்கு நீங்கள் அளித்த அனுமதியானது, எங்கள் உருவாக்கத்தை மேம்படுத்த உதவும்.உங்கள் %s ஆட்-ஆன் திட்டம் மேம்படுத்தப்பட்டது.உங்கள் %s விலையில்லா வெள்ளோட்டம் ரத்து செய்யப்பட்டது.%s திட்டத்தில் உங்கள் கணக்கின் செயல்பாடு துவங்கியது.%sக்கான உங்கள் Affiliate விண்ணப்பம் ஏற்கப்பட்டது. உள்நுழைந்து %sல் உங்கள் affiliate areaவை அணுகவும்.உங்கள் affiliate கணக்கு தற்காலிகமாக இடைநிறுத்தப்பட்டுள்ளது.உங்கள் மின்னஞ்சல் சரிபார்க்கப்பட்டது - நன்றி!உங்கள் விலையில்லா வெள்ளோட்டம் முடிந்தது. %3$sஐ தொடர்ந்து பயன்படுத்த %1$s %2$sஇவற்றை மேம்படுத்துங்கள்.உங்கள் வெள்ளோட்டம் முடிந்தது. ஆனாலும் பிற விலையில்லா சேவைகளைத் தொடரலாம்.உங்கள் உரிமம் ரத்தானது. இதில் தவறேதும் உணர்ந்தால் உடனடியாக எங்கள் உதவியை அணுகவும்.உங்கள் உரிமம் முடிந்தது. %3$sஐ தொடர்ந்து பயன்படுத்த %1$s %2$s இவற்றை மேம்படுத்துங்கள்.உங்கள் உரிமம் முடிந்தது. எனினும் நீங்கள் %sன் வசதிகளைத் தொடரலாம். எனினும், தொடர் மேம்படுத்தல் மற்றும் உதவிக்கு உங்கள் உரிமத்தைப் புதுப்பிக்கவும்.உங்கள் உரிமம் முடிந்தது. ஆனாலும் %sன் விலையில்லாப் பதிப்பை என்றும் தொடரலாம். உங்கள் உரிமம் செயல்படுத்தப்பட்டது.உங்கள் உரிமம் செயல்நிறுத்தப்பட்டது, %s திட்டத்திற்கு மாற்றப்பட்டுள்ளீர்கள்.உங்கள் பெயர் ஏற்றப்பட்டது.உங்கள் தேர்ந்தெடுத்த திட்டம் துவங்கியது.உங்கள் தேர்ந்தெடுத்த திட்டம் %sக்கு மாறியது.உங்கள் தேர்ந்தெடுத்த திட்டம் மேம்படுத்தப்பட்டது.உங்கள் சந்தா ரத்து செய்யப்பட்டது. உங்கள் %s திட்டம் %s அன்று காலாவதியாகிறது.உங்கள் வெள்ளோட்டம் துவங்கியதுZIP / தபால் குறியீடுRight onசெயல்பாட்டில்%s இல்லாமல் %s இயங்காதுப்ளக்இன் இல்லாமல் %s இயங்காதுHeads upஅனுமதி%s இருக்கிறதுசெயல்படுத்துகிறதுவருடம்APIபோய்த் தொலைதவறை சோதிக்கிறதுவாழ்த்துக்கள்தடுக்கப்பட்டதுஇணைக்கப்பட்டதுஅண்மைய பதிப்பை பதிவிறக்கஅண்மைய விலையில்லா பதிப்பை பதிவிறக்கமாதாமாதம்காலாவதிவழிமின்னஞ்சல் அனுப்பப்படுகிறதுமாதம்ஆண்டுஆண்டுக்காண்டுஒருமுறைதிட்டம்No SecretSDK பதிப்புகள்உரிமம்SyncSync Licenseஉருவாக்கியவர்OffOn%s அடிப்படையிலானதுவிலையில்லா வெள்ளோட்டம் தொடங்கட்டும்... டும்போய்த் தொலைபோய்த் தொலைசெயல்நிறுத்தப்படுகிறதுdelegateபாதுகாப்பு & மேம்படுத்தல், விளக்கவுரை மற்றும் தள்ளுபடி விவரங்களை எனக்கு அனுப்பவும். %s செய்க, %s தேவையில்லை%s திட்டம்%s பில் எழுதப்பட்டதுசிறப்புHeyஅரே ஓ சம்போ!வணக்கம் %s,நிறுவப்பட்டதுYee-hawஉரிமம்தளங்கள்msபுதிய பீட்டா பதிப்புபுதிய பதிப்புஉறுதிப்படுத்தப்படவில்லைவிலைவிலை விவரம்பதிப்புsecபாதுகாப்பு & மேம்படுத்தல், விளக்கவுரை மற்றும் தள்ளுபடி விவரங்களை எனக்கு அனுப்பவும்.கடந்திடுHmmவெள்ளோட்டம் துவங்கலாம்சந்தாswitching%sன் அண்மைய பதிப்பு இதோவெள்ளோட்டம்வெள்ளோட்டம்அழிதரமிறக்குதிருத்துமறைத்திடுதெரிவு செய்தெரிவை அகற்றுவாங்குககாட்டுகடந்திடுமேம்படுத்துமேம்படுத்து%s முன்புfreemius/languages/freemius-ru_RU.mo000064400000134002147600046700013543 0ustar00 S%A g s6_r#   '29AJRH[  *4CXkr5z  '  $.?F2 !H aj 0  !&!5!=!Q!Y!b! g!u!!!! ! !Y!2"A" R"^"g"l"(|"1"%""### +# 6#C#Y# a#k# p#{# # #######$-$ I$T$$ $%Y%a^%%%% % %; &F&K&R&Q' V' a' n'{'j'' ((3(K(~_(U(4)P)i)))-))S)D*'\**7*g*'+ -+9+L+b+u+ }+1+.+O+8,A,y,t-a--B-,=.j. o. |.. ... . ..4./ !/+///6/>/ E/Q/ X/ d/p//// ///%/+/0 00 >0/K0{0m0000 1)1:1W14`1151(112-2F2UZ221y3[34&4-4 =4G4(V4*4"414+4*+5&V5N}5555.5)!6K6[6{66 6 6666 6666W7f7o777777 7 757l8&p888 8888 88&8L9fj99 999 9: :: 2:?:P:: };\;;; <C,<p<<<d,=-== ==M=G'> o>y>>>>>E>>>??&?-?*CC&CZD.rD.D9DZ E3eE<EUE,FKF(GGHII=I$SIxIIII&I*J:JQJoJ3JJJJJK*0K1[KKK#KKKL L+LKLbL wLLLLLLL1M@MTM dM qM |MM MQMM NN4N GNSN bN lN vN N N N N N N NYN61TDhTTTT^TpPUVE\W4WWWW XX2XIX XXcX*Y.Y JYXYjYY"Y;Y)Y5)Z_ZwZ$Z6Z$Z"[]:[ [[[i[DJ\\\*\\\^65^Wl^>^_ `6`0``a#aAa^axa a@a a$b"*b/Mb}bb"c3cc d ddb9dxdee{eee9eef0"f Sfaf uff.ff0f&g;g7NgBgg g3g,.h[h!mh"i"i iiwj mkzk/kBk9 lxCll lYl-o9Co"}o o-oopppyp5Jqq{r6as9s;s^tQmttt,u:uurvtv%w"+w3Nw%w w ww_wTExTxwxlgzz({{ d|n|hH}}(}$}6~J~ d~r~"~!~'~b~4T" ; () R_v' =ƀ*w0zB#9fb4. cpT9քUud !Nk)bFv\A Q/_" ŋ/1(Zn #@3tQJ֍!;> z5 ώ3$$I ]i,A,Őؐ ܐ)' BqNO46K( (ۓ8=ޔ *Õ+ $:N:m5ޘ%ș4 >88*B؜ *)TodnԝC` r }Ğ.\2;!#KEL(:97/q$8Ƣ T1 ХP%X~5/Ԧ IX!0GR8Ө'Y.9`Ǫt(T&|{H˭L<:ưNGP> ױ#;Xj!߲.*Y m T4E`w!, )1[k{  ƵԵܵ 1E N\c*k-޶ &BTp ̷%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is the new owner of the account.%s minimum payout amount.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s tracking cookie after the first visit to maximize earnings potential.APIAccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate Free VersionActivate LicenseActivate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.Address Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.Anonymous feedbackApply to become an affiliateAre you sure you want to delete all Freemius data?Are you sure you want to proceed?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBillingBlockingBodyBusiness nameCan't find your license key?CancelCancel InstallationCancel SubscriptionCancel TrialCancelledCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanCheckoutCityClear API CacheClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivationDebug LogDelete All AccountsDetailsDon't have a license key?Donate to this pluginDownloadDownload %s VersionDownload the latest %s versionDownload the latest versionDownloadedDue to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.EmailEmail addressEndEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used for the upgrade below and we will resend you the license key.ErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.FreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFull nameFunctionGet commission for automated subscription renewals.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.In %sInstall NowInstall Update NowInstalling plugin: %sInvalid module ID.InvoiceIs ActiveIt looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's not what I was looking forJust letting you know that the add-ons information of %s is being pulled from an external server.KeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Version InstalledLearn moreLengthLicenseLicense KeyLicense keyLifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerMessageMethodMobile appsModuleModule PathModule TypeMore information about %sNameNewNewer Version (%s) InstalledNewsletterNextNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Opt InOpt OutOtherPCI compliantPaid add-on must be deployed to Freemius.PayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProcess IDProcessingProgram SummaryPromotion methodsProvincePublic KeyPurchase LicenseQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!RequestsRequires WordPress VersionResultSDKSDK PathSave %sScheduled CronsScreenshotsSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSingle Site LicenseSite IDSitesSkip & %sSlugSocial media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart TrialStart my free %sStateSubmit & %sSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThis plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.TimestampTitleTotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser IDUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWebsite, email, and social media statistics (optional)What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress.org Plugin PageYes - %sYou already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou do not have a valid license to access the premium version.You have a %s license.You have successfully updated your %s.You might have missed it, but you don't have to share any data and can just %s the opt-in.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdeactivatinge.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,interjection expressing joy or exuberanceYee-hawlike websitesSitesmillisecondsmsnot verifiednounPricenounPricingproduct versionVersionsecondssecsomething somebody says when they are thinking about what you have just said.Hmmstart the trialswitchingthe latest %s version heretrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Leo Fajardo , 2021 Language-Team: Russian (Russia) (http://app.transifex.com/freemius/wordpress-sdk/language/ru_RU/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: ru_RU Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3); X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 Закончить активацию %s сейчас Покупка %s плагина успешно состоялась%s установок %s лицензий %s тому назад %s вознаграждения, если клиент купит новую лицензию.Бесплатный период пользования %s закончился. Этот плагин является премиум продуктом и он был деактивирован автоматически. Если Вы планируете дальнейшее его использование, пожалуйста купите лицензию. %s является премиум продуктом. Необходимо купить лицензию перед активацией плагина. %я является новым владельцем аккаунта%s минимальная сумма выплаты %s или выше%s оценка %s оценки %s секунд %звездочка %s звездочки %s время %s раз %s данные cookies предоставляются после первого посещения, чтобы максимально увеличить вероятность заработка. APIЛичный кабинет ДеталиДействия Активировать Активировать %sАктивируйте план %sАктивировать бесплатную версию?Активировать лицензиюАктивируйте этот функционал Активирован Функционал для %sФункционал модуля %sДобавьте другое доменное имя Функционал плагина Настройки плагина Функционал должен быть заявлен на WordPress.org или Freemius Поле для адреса %dПартнерПартнерство После окончания Вашего бесплатного %s, платите всего лиш %sСогласиться и активировать лицензию Все запросы Все типыРазрешить и продолжитьКоличество Автоматическое скачивание и установка %s ( платной версии) %s начнется через %s. Если Вы хотите все сделать в ручном режиме, нажмите на кнопку "Отменить" сейчас. Анонимный отзыв Подать заявку на партнерство Вы уверенны, что хотите удалить все данные Freemius?Вы уверены, что хотите продолжить?Мы выделяем 30 дней для поступления возвратов и поэтому вознаграждения выплачиваются за покупки, которые были совершены более чем 30 дней назад.Авто установка работает только для зарегистрированных пользователей.Автоматическое продление в %sАвтоматическая установка Средний рейтинг Отлично!Стать партнеромСистема оплаты Блокирование Основная часть Название бизнеса Не можете найти лицензионный ключ? Отмена Отменить установку Отменить подписку Отменить тестовый период Аннулирована Отказ от пользования тестовым периодом автоматически блокирует доступ ко всем премиум возможностям. Вы уверены, что хотите отказаться?Изменить лицензию Сменить владельца лицензии Изменить план Оплата Город Очистить кэш APIНажмите здесь, чтобы пользоваться плагином анонимно. Нажмите, чтобы посмотреть отзывы, которые сформировали рейтинг %sКликните, чтобы посмотреть снимок %d на широком экране. КодСовместима с Свяжитесь с намиСвязаться со службой поддержкиКонтакты Контрибьюторы Невозможно активировать %sСтрана Тип задачиДата Деактивировать Деактивировать лицензию ДеактивацияЖурнал устранения ошибок Удалить все аккаунтыДетальнейУ Вас нет лицензионного ключа?Инвестировать в разработку плагина Скачать Скачайте версию %sСкачайте последнюю версию %sСкачай последнюю версиюЗагружен Из-за нарушения условий партнерства мы вынуждены временно заблокировать Ваш аккаунт. Если у Вас возникли вопросы, пожалуйста, обратитесь в службу поддержки. Электронный адрес Электронный адрес Конец Введите домен Вашего сайта или других сайтов на которых Вы намерены продвигать %s.Введите ниже адрес своей электронной почты, которую Вы использовали для обновлений и мы Вам отправим повторно Ваш лицензионный ключ. ОшибкаОшибка сервераСрок действия закончился Окончание срока пользования через %sДополнительные доменные имена Дополнительные доменные имена, где Вы будете продвигать продукт. ФайлФильтр В соответствии с руководством WordPress.org, перед началом тестового периода мы просим, чтобы Вы присоединились к нашему сообществу предоставив информацию о Вашем сайте и также Ваши личные данные, тем самым разрешив %s периодически отправлять сообщения на %s для уведомлений об обновлениях и подтверждения Вашего тестового периода. Бесплатная Бесплатный период пользования Бесплатная версия Freemius APIИсправление ошибок FreemiusFreemius SDK не удалось найти основной файл плагина. Пожалуйста, свяжитесь с sdk@freemius.com с текущей ошибкой.Cостояние Freemius Полное имяФункция Получай вознаграждение за автоматические продления пользования. У Вас есть лицензионный ключ?Привет! Знали ли Вы, что %s предоставляет реферальную программу? Если Вам нравится %s, Вы можете стать нашим представителем и зарабатывать!Тебе нравится пользоваться %s? Воспользуйся всеми нашими премиум возможностями на протяжении %d - дневного тестового периода. Как загрузить и активировать?Как Вы намерены продвигать нас?Я больше не могу оплачивать это. Я не могу понять как сделать так, чтобы оно работалоЯ не хочу делиться личной информацией с ВамиЯ нашел лучший %sЯ провел апгрейд аккаунта, но при попытке синхронизировать лицензию, мой тарифный план не меняется. %s больше не понадобится.%s требовалась на короткое времяIDЕсли у Вас есть время, пожалуйста, сообщите причину почему Вы %sЕсли Вы передаете права пользования аккаунтом %s %s нажмите кнопку " Сменить права использования"В %sУстановить сейчас Провести обновления сейчас Установка плагина: %sНеверный ID модуляСчет активный Вероятно возникли трудности с активацией лицензии. Вероятно деактивация лицензии не состоялась. Возможно, Ваш тестовый период уже закончился. Вероятно Вы все еще пользуетесь сервисом согласно плану %s. Если Вы обновляли или меняли свой тарифный план, то вероятно существуют какие-то трудности связанные с Вашим программным обеспечением. Извините. Вероятно Ваш сайт не использует активную лицензию сейчас. Вероятно один из параметров является неверным. Обновите свой Public Key, Secret Key&User ID и повторите попытку.Это не то, что я искал. Сообщаем, что информация о дополнительных настройках %s предоставляется со стороннего сервера. Ключ Пожалуйста, сообщите о функционале, который не работает, чтобы мы смогли исправить его для дальнейшего использования. Пожалуйста, укажите причину, чтобы мы могли исправиться. Последний Последнее обновление Последняя лицензия Последняя версия установленаУзнать большеДлинна Лицензия Лицензионный ключ Лицензионный ключНа бессрочный период Вам нравится %s? Стань нашим партнером и зарабатывай ;-)Загрузить опцию базы данных Локальный хостинг Журнал изменений Программа сохранения изменений Сообщение Метод Мобильные приложения МодульПуть модуля Тип модуля Больше информации о %sИмяНовое Более новая версия %s установлена Рассылка Следующий No IDБез обязательств платить %s - аннулируй пользование в любое время Бесплатное пользование на протяжении %s дней. Отмена в любое время. Не требуются данные платежной картыБессрочный период пользования Бессрочный Тарифные планы %s не предусматривают тестовый период. O.K.По окончанию срока действия Вашей лицензии, Вы сможете пользоваться бесплатной версией, но у Вас не будет доступа к возможностям %s. ПрисоединитьсяОтказаться от использованияДругиеЖалоба PCIПлатный функционал должен быть заявлен в FreemiusЭлектронный адрес аккаунта PayPalПлатежиВыплаты производятся в долларах США через PayPal.Тарифный план Тарифного плана %s не существует, поэтому Вы не можете начать тестовый период. Тарифный план %s не предусматривает тестового периода. ID тарифного плана Пожалуйста, напишите нам сообщение здесь. Пожалуйста, напишите нам сообщение следующего содержания:Пожалуйста, скачайте %sПожалуйста введите лицензионный ключ, который Вы получили на электронный адрес сразу после покупки. Пожалуйста, предоставьте соответственную статистику вебсайта или страницы социальных сетей, например, количество уникальных посетителей, количество подписчиков, читателей, т. д. ( эта информация останется конфиденциальной). Пожалуйста, пройдите эти шаги для того, чтобы произвести апгрейдПожалуйста, предоставьте максимально детальную информацию о том, как Вы планируете продвигать %s.Пожалуйста, введите Ваше полное имяПлагин Главная страница плагина ID плагина Установка плагина Журнал изменений Описание Часто задаваемые вопросы Функционал&тарифные планы Установка Другие заметки Отзывы Плагин является 'Serviсeware'. Это означает, что он не имеет премиум версию кода. Плагины Синхронизация плагинов и шаблонов Премиум Премиум версия %s была успешно активирована. Премиум версия плагина была установленаПремиум версия Премиум версия уже активированаЦены Политика КонфиденциальностиID процесса Обработка данных Краткое описание программы Методы продвижения Провинция Public Key Купите лицензию Выделенный объем памятиОтправить письмо активации еще раз Порекомендуй %s новым пользователям и зарабатывай %s c каждой успешной продажи. Запросы Необходима версия WordPress РезультатSDKпуть SDKЭкономия %sСпланированные задачиСнимки экрана Secret Key Безопасная страница HTTPS %s воспроизводится с внешнего ресурса К сожалению у нас возникли трудности с отменой Вашего тестового периода. Пожалуйста, повторите попытку через несколько минут.Вероятно, Вы пользуетесь последней версиейВыбрать страну Отправить лицензионный ключУстановить опцию базы данных Лицензия на один сайт Site IDСайтов Пропустить & %sОписательная часть URL Социальные сети ( Facebook, Twitter, etc.)Извините за неудобство. Мы будем рады помочь, если Вы нам предоставите эту возможность. Извините, нам не удалось обновить электронный адрес. Другой пользователь с таким же адресом уже был зарегистрирован. НачалоНачать тестовый периодНачать мой бесплатный %sШтат Отправить&%sПоддержка Форум поддержки Синхронизация данных с сервера ID налога/НДС Пользовательское соглашениеСпасибо за подачу заявки на партнерство. К сожалению, мы приняли решение отказать Вам в этой возможности. Пожалуйста, повторите попытку через 30 дней. Спасибо за подачу заявки на партнерство. Мы рассмотрим Ваши данные на протяжении следующих 14 дней и свяжемся с Вами. Спасибо %sСпасибо, что подтвердили изменение прав использования. Вам отправлено письмо на %s для окончательного подтверждения. %s повредила мой сайт%s не сработала%s не сработала как ожидалось%s отличная возможность, но мне нужен определенный функционал, который вы не поддерживаете. %s не работает%s внезапно перестала работать Процесс установки уже начат и может занять несколько минут. Пожалуйста, подождите окончания процесса и не обновляйте эту страницу. Удаленный пакет плагинов не содержит папку с нужным описанием URL и смена имени не срабатывает. Обновление %s было успешно завершеноШаблон Переключатель шаблона Шаблоны Этот плагин не отмечен как совместимый з Вашей версией WordPress Этот плагин не был тестирован с Вашей текущей версией WordPress. Маркер времени Название ИтогоНаселенный пункт Тестовый период ТипНевозможно присоединиться к системе файлов. Пожалуйста, подтвердите свои данные. Неограниченная лицензия Неограниченные обновления Неограниченное вознаграждение до %s сайтов Обновить Обновить лицензиюНовости, объявления, маркетинг, без спамаСделать апгрейд Загрузите и активируйте скачанную версиюВау!User ID Пользователи Значение Письмо подтверждение было только что отправлено на %s. Если Вы не получите его через 5 минут, пожалуйста, проверьте папку спам.Подтвержден Подтвердите электронный адрес Релиз версии %s состоялся. Смотреть детальней Просмотр платных возможностейПредупреждение Активная лицензия выданная на этот электронный адрес не была найдена. Вы уверены, что предоставили правильный электронный адрес?К сожалению, Ваш почтовый адрес не найден в системе. Вы уверены, что предоставили правильный адрес? Мы усовершенствовали в %s, %s для лучшей работы Вебсайт, электронный адрес и статистика социальных сетей (не обязательно)Каковы были Ваши ожидания? Какой функционал?Какой Ваш %s?Какая стоимость была бы для Вас приемлемой? Что именно Вы ищите? Какое название %s?Где Вы намерены продвигать %s?Страница плагинов WordPress.orgДа - %sВы уже использовали Ваш тестовый периодВы уже на расстоянии одного клика от начала Вашего бесплатного %1$s - дневного тестового периода по тарифному плану %2$s. Все прошло хорошо!Вы уже пользуетесь тестовой версией %s Вам осталось совсем немножко %sУ Вас нет необходимых лицензионных прав для пользования премиум версиейУ Вас есть лицензия %s.Вы успешно обновили Ваш %sВозможно, Вы не обратили внимание, но Вы не обязаны делиться никакими данными и можете просто %s кнопку "Присоединиться". Ваш %s план был успешно обновленВаш бесплатный тестовый период был успешно отменен. Ваша учетная запись была успешно активирована согласно плану %sВаша заявка на партнерство с %s принята! Войдите в Ваш кабинет партнера на %sВаш партнерский аккаунт временно недоступен. Ваш электронный адрес был успешно подтвержден и Вы просто молодец!Ваша лицензия была аннулирована. Если Вы считаете, что это ошибка, пожалуйста свяжитесь с нашей службой поддержки. Срок действия Вашей лицензии закончен. Вы можете продолжать пользоваться всеми возможностями %s продлив Вашу лицензию. Вы также будете получать доступ к обновлениям и поддержке. Срок действия Вашей лицензии закончился. Вы можете продолжать пользоваться бесплатной версией %s на бессрочной основе.Ваша лицензия была успешно активирована. Ваша лицензия была успешно деактивирована и Вы снова пользуетесь планом %s.Ваше имя было успешно обновленоВаш тарифный план был успешно изменен на %s.Ваш тарифный план был успешно изменен. Ваш тестовый период успешно начатИндекс Все верно!%s не работает без %s.%s не может работать без плагина. Внимание!Осталось %s Активация на один год APIЗакрыть Устранение ошибокПоздравления! Заблокировано Соединено Скачать последнюю версиюПомесячно Срок пользования Путь Электронное письмо отправляется Вам на почту на один месяцЕжегодно Один раз в год Один раз Тарифный план Нет секрета Версии SDKЛицензия Синхронизировать Синхронизация лицензии АвторВыключить Включить Основан на %sНачни тестовый период!Закрыть Закрыть Деактивация %s план Оплачивать %sЛучший Привет!Упс!Здравствуйте %sУра!Сайтов мс не подтвержден Стоимость ЦеныВерсия секХм...Начать тестовый периодПереключение Последняя версия %s здесьТестовый периодУдалитьПонизить план Редактировать Спрятать Присоединится Отписаться КупитьПоказать ПропуститьОбновить Сделать апгрейд%s тому назад freemius/languages/freemius.pot000064400000236263147600046700012714 0ustar00# Copyright (C) 2024 freemius # This file is distributed under the same license as the freemius package. msgid "" msgstr "" "Project-Id-Version: freemius\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language-Team: Freemius Team \n" "Last-Translator: Vova Feldman \n" "POT-Creation-Date: 2024-10-20 12:05+0000\n" "Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues\n" "X-Poedit-Basepath: ..\n" "X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c\n" "X-Poedit-SearchPath-0: .\n" "X-Poedit-SearchPathExcluded-0: *.js\n" "X-Poedit-SourceCharset: UTF-8\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: includes/class-freemius.php:1790, templates/account.php:943 msgid "An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned." msgstr "" #: includes/class-freemius.php:1797 msgid "Would you like to proceed with the update?" msgstr "" #: includes/class-freemius.php:2022 msgid "Freemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error." msgstr "" #: includes/class-freemius.php:2024, includes/fs-plugin-info-dialog.php:1513 msgid "Error" msgstr "" #: includes/class-freemius.php:2470 msgid "I found a better %s" msgstr "" #: includes/class-freemius.php:2472 msgid "What's the %s's name?" msgstr "" #: includes/class-freemius.php:2478 msgid "It's a temporary %s - I'm troubleshooting an issue" msgstr "" #: includes/class-freemius.php:2480 msgid "Deactivation" msgstr "" #: includes/class-freemius.php:2481 msgid "Theme Switch" msgstr "" #: includes/class-freemius.php:2490, templates/forms/resend-key.php:24, templates/forms/user-change.php:29 msgid "Other" msgstr "" #: includes/class-freemius.php:2498 msgid "I no longer need the %s" msgstr "" #: includes/class-freemius.php:2505 msgid "I only needed the %s for a short period" msgstr "" #: includes/class-freemius.php:2511 msgid "The %s broke my site" msgstr "" #: includes/class-freemius.php:2518 msgid "The %s suddenly stopped working" msgstr "" #: includes/class-freemius.php:2528 msgid "I can't pay for it anymore" msgstr "" #: includes/class-freemius.php:2530 msgid "What price would you feel comfortable paying?" msgstr "" #: includes/class-freemius.php:2536 msgid "I don't like to share my information with you" msgstr "" #: includes/class-freemius.php:2557 msgid "The %s didn't work" msgstr "" #: includes/class-freemius.php:2567 msgid "I couldn't understand how to make it work" msgstr "" #: includes/class-freemius.php:2575 msgid "The %s is great, but I need specific feature that you don't support" msgstr "" #: includes/class-freemius.php:2577 msgid "What feature?" msgstr "" #: includes/class-freemius.php:2581 msgid "The %s is not working" msgstr "" #: includes/class-freemius.php:2583 msgid "Kindly share what didn't work so we can fix it for future users..." msgstr "" #: includes/class-freemius.php:2587 msgid "It's not what I was looking for" msgstr "" #: includes/class-freemius.php:2589 msgid "What you've been looking for?" msgstr "" #: includes/class-freemius.php:2593 msgid "The %s didn't work as expected" msgstr "" #: includes/class-freemius.php:2595 msgid "What did you expect?" msgstr "" #. translators: %s: License type (e.g. you have a professional license) #: includes/class-freemius.php:4517 msgid "You have purchased a %s license." msgstr "" #: includes/class-freemius.php:4521 msgid " The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box." msgstr "" #: includes/class-freemius.php:4531, includes/class-freemius.php:20889, includes/class-freemius.php:24610 msgctxt "interjection expressing joy or exuberance" msgid "Yee-haw" msgstr "" #: includes/class-freemius.php:4545 msgctxt "addonX cannot run without pluginY" msgid "%s cannot run without %s." msgstr "" #: includes/class-freemius.php:4546 msgctxt "addonX cannot run..." msgid "%s cannot run without the plugin." msgstr "" #: includes/class-freemius.php:4548, includes/class-freemius.php:5745, includes/class-freemius.php:13477, includes/class-freemius.php:14228, includes/class-freemius.php:17997, includes/class-freemius.php:18117, includes/class-freemius.php:18294, includes/class-freemius.php:20620, includes/class-freemius.php:21736, includes/class-freemius.php:22772, includes/class-freemius.php:22902, includes/class-freemius.php:23045, templates/add-ons.php:57 msgctxt "exclamation" msgid "Oops" msgstr "" #: includes/class-freemius.php:4827 msgid "There was an unexpected API error while processing your request. Please try again in a few minutes and if it still doesn't work, contact the %s's author with the following:" msgstr "" #. translators: %s: License type (e.g. you have a professional license) #: includes/class-freemius.php:5437 msgid "You have a %s license." msgstr "" #: includes/class-freemius.php:5410 msgid "Premium %s version was successfully activated." msgstr "" #: includes/class-freemius.php:5422, includes/class-freemius.php:7434 msgctxt "Used to express elation, enthusiasm, or triumph (especially in electronic communication)." msgid "W00t" msgstr "" #: includes/class-freemius.php:5728 msgid "%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license." msgstr "" #: includes/class-freemius.php:5732 msgid "%s is a premium only add-on. You have to purchase a license first before activating the plugin." msgstr "" #: includes/class-freemius.php:5741, templates/add-ons.php:186, templates/account/partials/addon.php:386 msgid "More information about %s" msgstr "" #: includes/class-freemius.php:5742 msgid "Purchase License" msgstr "" #. translators: %3$s: What the user is expected to receive via email (e.g.: "the installation instructions" or "a license key") #: includes/class-freemius.php:6747 msgid "You should receive %3$s for %1$s to your mailbox at %2$s in the next 5 minutes." msgstr "" #: includes/class-freemius.php:6756 msgctxt "Part of the message telling the user what they should receive via email." msgid "a license key" msgstr "" #. translators: %s: activation link (e.g.: Click here) #: includes/class-freemius.php:6764 msgid "%s to activate the license once you get it." msgstr "" #: includes/class-freemius.php:6772 msgctxt "Part of an activation link message." msgid "Click here" msgstr "" #: includes/class-freemius.php:6750 msgctxt "Part of the message telling the user what they should receive via email." msgid "the installation instructions" msgstr "" #: includes/class-freemius.php:6779 msgctxt "Part of the message that tells the user to check their spam folder for a specific email." msgid "the product's support email address" msgstr "" #: includes/class-freemius.php:6785 msgid "If you didn't get the email, try checking your spam folder or search for emails from %4$s." msgstr "" #: includes/class-freemius.php:6787 msgid "Thanks for upgrading." msgstr "" #: includes/class-freemius.php:6738 msgid "You should receive a confirmation email for %1$s to your mailbox at %2$s. Please make sure you click the button in that email to %3$s." msgstr "" #: includes/class-freemius.php:6741 msgid "start the trial" msgstr "" #: includes/class-freemius.php:6742, templates/connect.php:208 msgid "complete the opt-in" msgstr "" #: includes/class-freemius.php:6744 msgid "Thanks!" msgstr "" #: includes/class-freemius.php:6923 msgid "You are just one step away - %s" msgstr "" #: includes/class-freemius.php:6926 msgctxt "%s - plugin name. As complete \"PluginX\" activation now" msgid "Complete \"%s\" Activation Now" msgstr "" #: includes/class-freemius.php:7008 msgid "We made a few tweaks to the %s, %s" msgstr "" #: includes/class-freemius.php:7012 msgid "Opt in to make \"%s\" better!" msgstr "" #: includes/class-freemius.php:7433 msgid "The upgrade of %s was successfully completed." msgstr "" #: includes/class-freemius.php:10196, includes/class-fs-plugin-updater.php:1142, includes/class-fs-plugin-updater.php:1364, includes/class-fs-plugin-updater.php:1357, templates/auto-installation.php:32 msgid "Add-On" msgstr "" #: includes/class-freemius.php:10198, templates/account.php:407, templates/account.php:415, templates/debug.php:458, templates/debug.php:678 msgid "Plugin" msgstr "" #: includes/class-freemius.php:10199, templates/account.php:408, templates/account.php:416, templates/debug.php:458, templates/debug.php:678, templates/forms/deactivation/form.php:107 msgid "Theme" msgstr "" #: includes/class-freemius.php:13284 msgid "An unknown error has occurred while trying to toggle the license's white-label mode." msgstr "" #: includes/class-freemius.php:13298 msgid "Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s." msgstr "" #: includes/class-freemius.php:13303, templates/account/partials/disconnect-button.php:84 msgid "User Dashboard" msgstr "" #: includes/class-freemius.php:13304 msgid "revert it now" msgstr "" #: includes/class-freemius.php:13362 msgid "An unknown error has occurred while trying to set the user's beta mode." msgstr "" #: includes/class-freemius.php:13448 msgid "Invalid new user ID or email address." msgstr "" #: includes/class-freemius.php:13478 msgid "Sorry, we could not complete the email update. Another user with the same email is already registered." msgstr "" #: includes/class-freemius.php:13479 msgid "If you would like to give up the ownership of the %s's account to %s click the Change Ownership button." msgstr "" #: includes/class-freemius.php:13486 msgid "Change Ownership" msgstr "" #: includes/class-freemius.php:14095 msgid "Invalid site details collection." msgstr "" #: includes/class-freemius.php:14217 msgid "We can't see any active licenses associated with that email address, are you sure it's the right address?" msgstr "" #: includes/class-freemius.php:14215 msgid "We couldn't find your email address in the system, are you sure it's the right address?" msgstr "" #: includes/class-freemius.php:14521 msgid "Account is pending activation. Please check your email and click the link to activate your account and then submit the affiliate form again." msgstr "" #: includes/class-freemius.php:14647, templates/forms/premium-versions-upgrade-handler.php:46 msgid "Renew your license now" msgstr "" #: includes/class-freemius.php:14635, templates/forms/premium-versions-upgrade-handler.php:47 msgid "Buy a license now" msgstr "" #: includes/class-freemius.php:14651 msgid "%s to access version %s security & feature updates, and support." msgstr "" #: includes/class-freemius.php:17337 msgid "%s opt-in was successfully completed." msgstr "" #: includes/class-freemius.php:17361, includes/class-freemius.php:21346 msgid "Your trial has been successfully started." msgstr "" #: includes/class-freemius.php:17351 msgid "Your account was successfully activated with the %s plan." msgstr "" #: includes/class-freemius.php:17995, includes/class-freemius.php:18115, includes/class-freemius.php:18292 msgid "Couldn't activate %s." msgstr "" #: includes/class-freemius.php:17996, includes/class-freemius.php:18116, includes/class-freemius.php:18293 msgid "Please contact us with the following message:" msgstr "" #: includes/class-freemius.php:18112, templates/forms/data-debug-mode.php:162 msgid "An unknown error has occurred." msgstr "" #: includes/class-freemius.php:18654, includes/class-freemius.php:24166 msgid "Upgrade" msgstr "" #: includes/class-freemius.php:18662 msgid "Pricing" msgstr "" #: includes/class-freemius.php:18660 msgid "Start Trial" msgstr "" #: includes/class-freemius.php:18742, includes/class-freemius.php:18744 msgid "Affiliation" msgstr "" #: includes/class-freemius.php:18772, includes/class-freemius.php:18774, templates/account.php:260, templates/debug.php:425 msgid "Account" msgstr "" #: includes/class-freemius.php:18800, includes/class-freemius.php:18789, includes/class-freemius.php:18791, includes/customizer/class-fs-customizer-support-section.php:60 msgid "Contact Us" msgstr "" #: includes/class-freemius.php:18814, includes/class-freemius.php:18816, includes/class-freemius.php:24180, templates/account.php:130, templates/account/partials/addon.php:49 msgid "Add-Ons" msgstr "" #: includes/class-freemius.php:18849 msgctxt "ASCII arrow left icon" msgid "←" msgstr "" #: includes/class-freemius.php:18849 msgctxt "ASCII arrow right icon" msgid "➤" msgstr "" #: includes/class-freemius.php:18867 msgctxt "noun" msgid "Pricing" msgstr "" #: includes/class-freemius.php:19083, includes/customizer/class-fs-customizer-support-section.php:67 msgid "Support Forum" msgstr "" #: includes/class-freemius.php:20114 msgid "Your email has been successfully verified - you are AWESOME!" msgstr "" #: includes/class-freemius.php:20115 msgctxt "a positive response" msgid "Right on" msgstr "" #: includes/class-freemius.php:20621 msgid "seems like the key you entered doesn't match our records." msgstr "" #: includes/class-freemius.php:20645 msgid "Debug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the \"Stop Debug\" link." msgstr "" #: includes/class-freemius.php:20880 msgid "Your %s Add-on plan was successfully upgraded." msgstr "" #. translators: %s:product name, e.g. Facebook add-on was successfully... #: includes/class-freemius.php:20882 msgid "%s Add-on was successfully purchased." msgstr "" #: includes/class-freemius.php:20885 msgid "Download the latest version" msgstr "" #: includes/class-freemius.php:21003 msgid "It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again." msgstr "" #: includes/class-freemius.php:21003, includes/class-freemius.php:21416, includes/class-freemius.php:21517, includes/class-freemius.php:21604 msgid "Error received from the server:" msgstr "" #: includes/class-freemius.php:21244, includes/class-freemius.php:21522, includes/class-freemius.php:21575, includes/class-freemius.php:21682 msgctxt "something somebody says when they are thinking about what you have just said." msgid "Hmm" msgstr "" #: includes/class-freemius.php:21257 msgid "It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry." msgstr "" #: includes/class-freemius.php:21258, templates/account.php:132, templates/add-ons.php:250, templates/account/partials/addon.php:51 msgctxt "trial period" msgid "Trial" msgstr "" #: includes/class-freemius.php:21263 msgid "I have upgraded my account but when I try to Sync the License, the plan remains %s." msgstr "" #: includes/class-freemius.php:21267, includes/class-freemius.php:21325 msgid "Please contact us here" msgstr "" #: includes/class-freemius.php:21295 msgid "Your plan was successfully changed to %s." msgstr "" #: includes/class-freemius.php:21311 msgid "Your license has expired. You can still continue using the free %s forever." msgstr "" #. translators: %1$s: product title; %2$s, %3$s: wrapping HTML anchor element; %4$s: 'plugin', 'theme', or 'add-on'. #: includes/class-freemius.php:21313 msgid "Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions." msgstr "" #: includes/class-freemius.php:21321 msgid "Your license has been cancelled. If you think it's a mistake, please contact support." msgstr "" #: includes/class-freemius.php:21334 msgid "Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support." msgstr "" #: includes/class-freemius.php:21360 msgid "Your free trial has expired. You can still continue using all our free features." msgstr "" #. translators: %1$s: product title; %2$s, %3$s: wrapping HTML anchor element; %4$s: 'plugin', 'theme', or 'add-on'. #: includes/class-freemius.php:21362 msgid "Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions." msgstr "" #: includes/class-freemius.php:21408 msgid "Your server is blocking the access to Freemius' API, which is crucial for %1$s synchronization. Please contact your host to whitelist the following domains:%2$s" msgstr "" #: includes/class-freemius.php:21410 msgid "Show error details" msgstr "" #: includes/class-freemius.php:21513 msgid "It looks like the license could not be activated." msgstr "" #: includes/class-freemius.php:21555 msgid "Your license was successfully activated." msgstr "" #: includes/class-freemius.php:21579 msgid "It looks like your site currently doesn't have an active license." msgstr "" #: includes/class-freemius.php:21603 msgid "It looks like the license deactivation failed." msgstr "" #: includes/class-freemius.php:21632 msgid "Your %s license was successfully deactivated." msgstr "" #: includes/class-freemius.php:21633 msgid "Your license was successfully deactivated, you are back to the %s plan." msgstr "" #: includes/class-freemius.php:21636 msgid "O.K" msgstr "" #: includes/class-freemius.php:21689 msgid "Seems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes." msgstr "" #: includes/class-freemius.php:21698 msgid "Your subscription was successfully cancelled. Your %s plan license will expire in %s." msgstr "" #: includes/class-freemius.php:21741 msgid "You are already running the %s in a trial mode." msgstr "" #: includes/class-freemius.php:21753 msgid "You already utilized a trial before." msgstr "" #: includes/class-freemius.php:21792 msgid "None of the %s's plans supports a trial period." msgstr "" #: includes/class-freemius.php:21768 msgid "Plan %s do not exist, therefore, can't start a trial." msgstr "" #: includes/class-freemius.php:21780 msgid "Plan %s does not support a trial period." msgstr "" #: includes/class-freemius.php:21854 msgid "It looks like you are not in trial mode anymore so there's nothing to cancel :)" msgstr "" #: includes/class-freemius.php:21890 msgid "Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes." msgstr "" #: includes/class-freemius.php:21909 msgid "Your %s free trial was successfully cancelled." msgstr "" #: includes/class-freemius.php:22256 msgid "Seems like you got the latest release." msgstr "" #: includes/class-freemius.php:22257 msgid "You are all good!" msgstr "" #: includes/class-freemius.php:22239 msgid "Version %s was released." msgstr "" #: includes/class-freemius.php:22239 msgid "Please download %s." msgstr "" #: includes/class-freemius.php:22246 msgid "the latest %s version here" msgstr "" #: includes/class-freemius.php:22251 msgid "New" msgstr "" #: includes/class-freemius.php:22660 msgid "Verification mail was just sent to %s. If you can't find it after 5 min, please check your spam box." msgstr "" #: includes/class-freemius.php:22800 msgid "Site successfully opted in." msgstr "" #: includes/class-freemius.php:22801, includes/class-freemius.php:23876 msgid "Awesome" msgstr "" #: includes/class-freemius.php:22827 msgid "Diagnostic data will no longer be sent from %s to %s." msgstr "" #: includes/class-freemius.php:22817 msgid "Sharing diagnostic data with %s helps to provide functionality that's more relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the plugin should be translated and tailored to." msgstr "" #: includes/class-freemius.php:22818 msgid "Thank you!" msgstr "" #: includes/class-freemius.php:22987 msgid "A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder." msgstr "" #: includes/class-freemius.php:22985 msgid "A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours." msgstr "" #: includes/class-freemius.php:22999 msgid "Thanks for confirming the ownership change. An email was just sent to %s for final approval." msgstr "" #: includes/class-freemius.php:23005 msgid "%s is the new owner of the account." msgstr "" #: includes/class-freemius.php:23007 msgctxt "as congratulations" msgid "Congrats" msgstr "" #: includes/class-freemius.php:23029 msgid "Your name was successfully updated." msgstr "" #: includes/class-freemius.php:23024 msgid "Please provide your full name." msgstr "" #. translators: %s: User's account property (e.g. email address, name) #: includes/class-freemius.php:23094 msgid "You have successfully updated your %s." msgstr "" #: includes/class-freemius.php:23158 msgid "Is this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin." msgstr "" #: includes/class-freemius.php:23161 msgid "Click here" msgstr "" #: includes/class-freemius.php:23198 msgid "Bundle" msgstr "" #: includes/class-freemius.php:23271 msgid "Just letting you know that the add-ons information of %s is being pulled from an external server." msgstr "" #: includes/class-freemius.php:23272 msgctxt "advance notice of something that will need attention." msgid "Heads up" msgstr "" #: includes/class-freemius.php:23916 msgctxt "exclamation" msgid "Hey" msgstr "" #: includes/class-freemius.php:23916 msgid "How do you like %s so far? Test all our %s premium features with a %d-day free trial." msgstr "" #: includes/class-freemius.php:23924 msgid "No commitment for %s days - cancel anytime!" msgstr "" #: includes/class-freemius.php:23925 msgid "No credit card required" msgstr "" #: includes/class-freemius.php:23932, templates/forms/trial-start.php:53 msgctxt "call to action" msgid "Start free trial" msgstr "" #: includes/class-freemius.php:24009 msgid "Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!" msgstr "" #: includes/class-freemius.php:24018 msgid "Learn more" msgstr "" #: includes/class-freemius.php:24204, templates/account.php:569, templates/account.php:721, templates/connect.php:211, templates/connect.php:439, includes/managers/class-fs-clone-manager.php:1295, templates/forms/license-activation.php:27, templates/account/partials/addon.php:326 msgid "Activate License" msgstr "" #: includes/class-freemius.php:24205, templates/account.php:663, templates/account.php:720, templates/account/partials/addon.php:327, templates/account/partials/site.php:273 msgid "Change License" msgstr "" #: includes/class-freemius.php:24320, includes/class-freemius.php:24314, templates/account/partials/site.php:49, templates/account/partials/site.php:170 msgid "Opt In" msgstr "" #: includes/class-freemius.php:24312, templates/account/partials/site.php:170 msgid "Opt Out" msgstr "" #: includes/class-freemius.php:24578 msgid "Please follow these steps to complete the upgrade" msgstr "" #. translators: %s: Plan title #: includes/class-freemius.php:24582 msgid "Download the latest %s version" msgstr "" #: includes/class-freemius.php:24586 msgid "Upload and activate the downloaded version" msgstr "" #: includes/class-freemius.php:24588 msgid "How to upload and activate?" msgstr "" #: includes/class-freemius.php:24555 msgid " The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s" msgstr "" #: includes/class-freemius.php:24565 msgid "Activate %s features" msgstr "" #: includes/class-freemius.php:24623 msgid "Your plan was successfully upgraded." msgstr "" #: includes/class-freemius.php:24624 msgid "Your plan was successfully activated." msgstr "" #: includes/class-freemius.php:24733 msgid "%sClick here%s to choose the sites where you'd like to activate the license on." msgstr "" #: includes/class-freemius.php:24902 msgid "Auto installation only works for opted-in users." msgstr "" #: includes/class-freemius.php:24912, includes/class-freemius.php:24945, includes/class-fs-plugin-updater.php:1336, includes/class-fs-plugin-updater.php:1350 msgid "Invalid module ID." msgstr "" #: includes/class-freemius.php:24953, includes/class-fs-plugin-updater.php:1371 msgid "Premium add-on version already installed." msgstr "" #: includes/class-freemius.php:24921, includes/class-fs-plugin-updater.php:1372 msgid "Premium version already active." msgstr "" #: includes/class-freemius.php:24928 msgid "You do not have a valid license to access the premium version." msgstr "" #: includes/class-freemius.php:24935 msgid "Plugin is a \"Serviceware\" which means it does not have a premium code version." msgstr "" #: includes/class-freemius.php:25313 msgid "View paid features" msgstr "" #: includes/class-freemius.php:25628 msgid "Thank you so much for using our products!" msgstr "" #: includes/class-freemius.php:25629 msgid "You've already opted-in to our usage-tracking, which helps us keep improving them." msgstr "" #: includes/class-freemius.php:25648 msgid "%s and its add-ons" msgstr "" #: includes/class-freemius.php:25657 msgid "Products" msgstr "" #: includes/class-freemius.php:25617 msgid "Thank you so much for using %s and its add-ons!" msgstr "" #: includes/class-freemius.php:25618 msgid "Thank you so much for using %s!" msgstr "" #: includes/class-freemius.php:25624 msgid "You've already opted-in to our usage-tracking, which helps us keep improving the %s." msgstr "" #: includes/class-freemius.php:25664, templates/connect.php:312 msgid "Yes" msgstr "" #: includes/class-freemius.php:25665, templates/connect.php:313 msgid "send me security & feature updates, educational content and offers." msgstr "" #: includes/class-freemius.php:25666, templates/connect.php:318 msgid "No" msgstr "" #: includes/class-freemius.php:25668, templates/connect.php:320 msgid "do %sNOT%s send me security & feature updates, educational content and offers." msgstr "" #: includes/class-freemius.php:25678 msgid "Due to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)" msgstr "" #: includes/class-freemius.php:25680, templates/connect.php:327 msgid "Please let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:" msgstr "" #: includes/class-freemius.php:25970 msgid "License key is empty." msgstr "" #: includes/class-fs-plugin-updater.php:247, templates/forms/premium-versions-upgrade-handler.php:57 msgid "Renew license" msgstr "" #: includes/class-fs-plugin-updater.php:252, templates/forms/premium-versions-upgrade-handler.php:58 msgid "Buy license" msgstr "" #: includes/class-fs-plugin-updater.php:405, includes/class-fs-plugin-updater.php:372 msgid "There is a %s of %s available." msgstr "" #: includes/class-fs-plugin-updater.php:410, includes/class-fs-plugin-updater.php:374 msgid "new Beta version" msgstr "" #: includes/class-fs-plugin-updater.php:411, includes/class-fs-plugin-updater.php:375 msgid "new version" msgstr "" #: includes/class-fs-plugin-updater.php:434 msgid "Important Upgrade Notice:" msgstr "" #: includes/class-fs-plugin-updater.php:1401 msgid "Installing plugin: %s" msgstr "" #: includes/class-fs-plugin-updater.php:1442 msgid "Unable to connect to the filesystem. Please confirm your credentials." msgstr "" #: includes/class-fs-plugin-updater.php:1624 msgid "The remote plugin package does not contain a folder with the desired slug and renaming did not work." msgstr "" #: includes/fs-plugin-info-dialog.php:542 msgid "Purchase More" msgstr "" #: includes/fs-plugin-info-dialog.php:543, templates/account/partials/addon.php:390 msgctxt "verb" msgid "Purchase" msgstr "" #. translators: %s: N-days trial #: includes/fs-plugin-info-dialog.php:547 msgid "Start my free %s" msgstr "" #: includes/fs-plugin-info-dialog.php:755 msgid "Install Free Version Now" msgstr "" #: includes/fs-plugin-info-dialog.php:756, templates/add-ons.php:323, templates/auto-installation.php:111, templates/account/partials/addon.php:423, templates/account/partials/addon.php:370 msgid "Install Now" msgstr "" #: includes/fs-plugin-info-dialog.php:745 msgid "Install Free Version Update Now" msgstr "" #: includes/fs-plugin-info-dialog.php:746, templates/account.php:652 msgid "Install Update Now" msgstr "" #: includes/fs-plugin-info-dialog.php:772 msgctxt "as download latest version" msgid "Download Latest Free Version" msgstr "" #: includes/fs-plugin-info-dialog.php:773, templates/account.php:110, templates/add-ons.php:37, templates/account/partials/addon.php:30 msgctxt "as download latest version" msgid "Download Latest" msgstr "" #: includes/fs-plugin-info-dialog.php:788, templates/add-ons.php:329, templates/account/partials/addon.php:417, templates/account/partials/addon.php:361 msgid "Activate this add-on" msgstr "" #: includes/fs-plugin-info-dialog.php:790, templates/connect.php:436 msgid "Activate Free Version" msgstr "" #: includes/fs-plugin-info-dialog.php:791, templates/account.php:134, templates/add-ons.php:330, templates/account/partials/addon.php:53 msgid "Activate" msgstr "" #: includes/fs-plugin-info-dialog.php:999 msgctxt "Plugin installer section title" msgid "Description" msgstr "" #: includes/fs-plugin-info-dialog.php:1000 msgctxt "Plugin installer section title" msgid "Installation" msgstr "" #: includes/fs-plugin-info-dialog.php:1001 msgctxt "Plugin installer section title" msgid "FAQ" msgstr "" #: includes/fs-plugin-info-dialog.php:1002, templates/plugin-info/description.php:55 msgid "Screenshots" msgstr "" #: includes/fs-plugin-info-dialog.php:1003 msgctxt "Plugin installer section title" msgid "Changelog" msgstr "" #: includes/fs-plugin-info-dialog.php:1004 msgctxt "Plugin installer section title" msgid "Reviews" msgstr "" #: includes/fs-plugin-info-dialog.php:1005 msgctxt "Plugin installer section title" msgid "Other Notes" msgstr "" #: includes/fs-plugin-info-dialog.php:1020 msgctxt "Plugin installer section title" msgid "Features & Pricing" msgstr "" #: includes/fs-plugin-info-dialog.php:1030 msgid "Plugin Install" msgstr "" #: includes/fs-plugin-info-dialog.php:1102 msgctxt "e.g. Professional Plan" msgid "%s Plan" msgstr "" #: includes/fs-plugin-info-dialog.php:1128 msgctxt "e.g. the best product" msgid "Best" msgstr "" #: includes/fs-plugin-info-dialog.php:1134, includes/fs-plugin-info-dialog.php:1154 msgctxt "as every month" msgid "Monthly" msgstr "" #: includes/fs-plugin-info-dialog.php:1137 msgctxt "as once a year" msgid "Annual" msgstr "" #: includes/fs-plugin-info-dialog.php:1140 msgid "Lifetime" msgstr "" #: includes/fs-plugin-info-dialog.php:1154, includes/fs-plugin-info-dialog.php:1156, includes/fs-plugin-info-dialog.php:1158 msgctxt "e.g. billed monthly" msgid "Billed %s" msgstr "" #: includes/fs-plugin-info-dialog.php:1156 msgctxt "as once a year" msgid "Annually" msgstr "" #: includes/fs-plugin-info-dialog.php:1158 msgctxt "as once a year" msgid "Once" msgstr "" #: includes/fs-plugin-info-dialog.php:1164 msgid "Single Site License" msgstr "" #: includes/fs-plugin-info-dialog.php:1166 msgid "Unlimited Licenses" msgstr "" #: includes/fs-plugin-info-dialog.php:1168 msgid "Up to %s Sites" msgstr "" #: includes/fs-plugin-info-dialog.php:1178, templates/plugin-info/features.php:82 msgctxt "as monthly period" msgid "mo" msgstr "" #: includes/fs-plugin-info-dialog.php:1185, templates/plugin-info/features.php:80 msgctxt "as annual period" msgid "year" msgstr "" #: includes/fs-plugin-info-dialog.php:1239 msgctxt "noun" msgid "Price" msgstr "" #. translators: %s: Discount (e.g. discount of $5 or 10%) #: includes/fs-plugin-info-dialog.php:1287 msgid "Save %s" msgstr "" #: includes/fs-plugin-info-dialog.php:1297 msgid "No commitment for %s - cancel anytime" msgstr "" #: includes/fs-plugin-info-dialog.php:1300 msgid "After your free %s, pay as little as %s" msgstr "" #: includes/fs-plugin-info-dialog.php:1311 msgid "Details" msgstr "" #: includes/fs-plugin-info-dialog.php:1315, templates/account.php:121, templates/debug.php:291, templates/debug.php:328, templates/debug.php:577, templates/account/partials/addon.php:41 msgctxt "product version" msgid "Version" msgstr "" #: includes/fs-plugin-info-dialog.php:1322 msgctxt "as the plugin author" msgid "Author" msgstr "" #: includes/fs-plugin-info-dialog.php:1329 msgid "Last Updated" msgstr "" #. translators: %s: time period (e.g. "2 hours" ago) #: includes/fs-plugin-info-dialog.php:1334, templates/account.php:540 msgctxt "x-ago" msgid "%s ago" msgstr "" #: includes/fs-plugin-info-dialog.php:1343 msgid "Requires WordPress Version" msgstr "" #. translators: %s: Version number. #: includes/fs-plugin-info-dialog.php:1346, includes/fs-plugin-info-dialog.php:1366 msgid "%s or higher" msgstr "" #: includes/fs-plugin-info-dialog.php:1354 msgid "Compatible up to" msgstr "" #: includes/fs-plugin-info-dialog.php:1362 msgid "Requires PHP Version" msgstr "" #: includes/fs-plugin-info-dialog.php:1375 msgid "Downloaded" msgstr "" #. translators: %s: 1 or One (Number of times downloaded) #: includes/fs-plugin-info-dialog.php:1379 msgid "%s time" msgstr "" #. translators: %s: Number of times downloaded #: includes/fs-plugin-info-dialog.php:1381 msgid "%s times" msgstr "" #: includes/fs-plugin-info-dialog.php:1392 msgid "WordPress.org Plugin Page" msgstr "" #: includes/fs-plugin-info-dialog.php:1401 msgid "Plugin Homepage" msgstr "" #: includes/fs-plugin-info-dialog.php:1410, includes/fs-plugin-info-dialog.php:1494 msgid "Donate to this plugin" msgstr "" #: includes/fs-plugin-info-dialog.php:1417 msgid "Average Rating" msgstr "" #: includes/fs-plugin-info-dialog.php:1424 msgid "based on %s" msgstr "" #. translators: %s: 1 or One #: includes/fs-plugin-info-dialog.php:1428 msgid "%s rating" msgstr "" #. translators: %s: Number larger than 1 #: includes/fs-plugin-info-dialog.php:1430 msgid "%s ratings" msgstr "" #. translators: %s: 1 or One #: includes/fs-plugin-info-dialog.php:1445 msgid "%s star" msgstr "" #. translators: %s: Number larger than 1 #: includes/fs-plugin-info-dialog.php:1447 msgid "%s stars" msgstr "" #. translators: %s: # of stars (e.g. 5 stars) #: includes/fs-plugin-info-dialog.php:1459 msgid "Click to see reviews that provided a rating of %s" msgstr "" #: includes/fs-plugin-info-dialog.php:1472 msgid "Contributors" msgstr "" #: includes/fs-plugin-info-dialog.php:1513 msgid "This plugin requires a newer version of PHP." msgstr "" #. translators: %s: URL to Update PHP page. #: includes/fs-plugin-info-dialog.php:1522 msgid "Click here to learn more about updating PHP." msgstr "" #: includes/fs-plugin-info-dialog.php:1538, includes/fs-plugin-info-dialog.php:1536 msgid "Warning" msgstr "" #: includes/fs-plugin-info-dialog.php:1538 msgid "This plugin has not been marked as compatible with your version of WordPress." msgstr "" #: includes/fs-plugin-info-dialog.php:1536 msgid "This plugin has not been tested with your current version of WordPress." msgstr "" #: includes/fs-plugin-info-dialog.php:1557 msgid "Paid add-on must be deployed to Freemius." msgstr "" #: includes/fs-plugin-info-dialog.php:1558 msgid "Add-on must be deployed to WordPress.org or Freemius." msgstr "" #: includes/fs-plugin-info-dialog.php:1587 msgid "Latest Version Installed" msgstr "" #: includes/fs-plugin-info-dialog.php:1588 msgid "Latest Free Version Installed" msgstr "" #: includes/fs-plugin-info-dialog.php:1579 msgid "Newer Version (%s) Installed" msgstr "" #: includes/fs-plugin-info-dialog.php:1580 msgid "Newer Free Version (%s) Installed" msgstr "" #: templates/account.php:111, templates/forms/subscription-cancellation.php:96, templates/account/partials/addon.php:31, templates/account/partials/site.php:313 msgid "Downgrading your plan" msgstr "" #: templates/account.php:112, templates/forms/subscription-cancellation.php:97, templates/account/partials/addon.php:32, templates/account/partials/site.php:314 msgid "Cancelling the subscription" msgstr "" #. translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription' #: templates/account.php:114, templates/forms/subscription-cancellation.php:99, templates/account/partials/addon.php:34, templates/account/partials/site.php:316 msgid "%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s." msgstr "" #: templates/account.php:115, templates/forms/subscription-cancellation.php:100, templates/account/partials/addon.php:35, templates/account/partials/site.php:317 msgid "Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price." msgstr "" #: templates/account.php:116, templates/forms/subscription-cancellation.php:106, templates/account/partials/addon.php:36 msgid "Cancelling the trial will immediately block access to all premium features. Are you sure?" msgstr "" #: templates/account.php:117, templates/forms/subscription-cancellation.php:101, templates/account/partials/addon.php:37, templates/account/partials/site.php:318 msgid "You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support." msgstr "" #: templates/account.php:118, templates/forms/subscription-cancellation.php:102, templates/account/partials/addon.php:38, templates/account/partials/site.php:319 msgid "Once your license expires you can still use the Free version but you will NOT have access to the %s features." msgstr "" #. translators: %s: Plan title (e.g. "Professional") #: templates/account.php:120, templates/account/partials/activate-license-button.php:31, templates/account/partials/addon.php:40 msgid "Activate %s Plan" msgstr "" #. translators: %s: Time period (e.g. Auto renews in "2 months") #: templates/account.php:123, templates/account/partials/addon.php:43, templates/account/partials/site.php:293 msgid "Auto renews in %s" msgstr "" #. translators: %s: Time period (e.g. Expires in "2 months") #: templates/account.php:125, templates/account/partials/addon.php:45, templates/account/partials/site.php:295 msgid "Expires in %s" msgstr "" #: templates/account.php:126 msgctxt "as synchronize license" msgid "Sync License" msgstr "" #: templates/account.php:127, templates/account/partials/addon.php:46 msgid "Cancel Trial" msgstr "" #: templates/account.php:128, templates/account/partials/addon.php:47 msgid "Change Plan" msgstr "" #: templates/account.php:129, templates/account/partials/addon.php:48 msgctxt "verb" msgid "Upgrade" msgstr "" #: templates/account.php:131, templates/account/partials/addon.php:50, templates/account/partials/site.php:320 msgctxt "verb" msgid "Downgrade" msgstr "" #: templates/account.php:133, templates/add-ons.php:246, templates/plugin-info/features.php:72, templates/account/partials/addon.php:52, templates/account/partials/site.php:33 msgid "Free" msgstr "" #: templates/account.php:135, templates/debug.php:471, includes/customizer/class-fs-customizer-upsell-control.php:110, templates/account/partials/addon.php:54 msgctxt "as product pricing plan" msgid "Plan" msgstr "" #: templates/account.php:136 msgid "Bundle Plan" msgstr "" #: templates/account.php:268 msgid "Free Trial" msgstr "" #: templates/account.php:279 msgid "Account Details" msgstr "" #: templates/account.php:288 msgid "Stop Debug" msgstr "" #: templates/account.php:286, templates/forms/data-debug-mode.php:33 msgid "Start Debug" msgstr "" #: templates/account.php:295 msgid "Billing & Invoices" msgstr "" #: templates/account.php:318, templates/account/partials/addon.php:236, templates/account/partials/deactivate-license-button.php:35 msgid "Deactivate License" msgstr "" #: templates/account.php:341, templates/forms/subscription-cancellation.php:125 msgid "Are you sure you want to proceed?" msgstr "" #: templates/account.php:341, templates/account/partials/addon.php:260 msgid "Cancel Subscription" msgstr "" #: templates/account.php:370, templates/account/partials/addon.php:345 msgctxt "as synchronize" msgid "Sync" msgstr "" #: templates/account.php:385, templates/debug.php:634 msgid "Name" msgstr "" #: templates/account.php:391, templates/debug.php:635 msgid "Email" msgstr "" #: templates/account.php:398, templates/debug.php:469, templates/debug.php:684 msgid "User ID" msgstr "" #: templates/account.php:416, templates/account.php:734, templates/account.php:785, templates/debug.php:326, templates/debug.php:463, templates/debug.php:574, templates/debug.php:633, templates/debug.php:682, templates/debug.php:761, templates/account/payments.php:35, templates/debug/logger.php:21 msgid "ID" msgstr "" #: templates/account.php:423 msgid "Site ID" msgstr "" #: templates/account.php:426 msgid "No ID" msgstr "" #: templates/account.php:431, templates/debug.php:333, templates/debug.php:472, templates/debug.php:578, templates/debug.php:637, templates/account/partials/site.php:228 msgid "Public Key" msgstr "" #: templates/account.php:437, templates/debug.php:473, templates/debug.php:579, templates/debug.php:638, templates/account/partials/site.php:241 msgid "Secret Key" msgstr "" #: templates/account.php:440 msgctxt "as secret encryption key missing" msgid "No Secret" msgstr "" #: templates/account.php:494, templates/debug.php:690, templates/account/partials/site.php:262 msgid "License Key" msgstr "" #: templates/account.php:467, templates/account/partials/site.php:122, templates/account/partials/site.php:120 msgid "Trial" msgstr "" #: templates/account.php:525 msgid "Join the Beta program" msgstr "" #: templates/account.php:531 msgid "not verified" msgstr "" #: templates/account.php:600 msgid "Free version" msgstr "" #: templates/account.php:598 msgid "Premium version" msgstr "" #: templates/account.php:540, templates/account/partials/addon.php:195 msgid "Expired" msgstr "" #: templates/account.php:612 msgid "Verify Email" msgstr "" #: templates/account.php:689, templates/forms/user-change.php:27 msgid "Change User" msgstr "" #: templates/account.php:676 msgid "What is your %s?" msgstr "" #: templates/account.php:684, templates/account/billing.php:21 msgctxt "verb" msgid "Edit" msgstr "" #: templates/account.php:660, templates/account.php:923, templates/account/partials/site.php:250, templates/account/partials/site.php:272 msgctxt "verb" msgid "Show" msgstr "" #: templates/account.php:626 msgid "Download %s Version" msgstr "" #: templates/account.php:642 msgid "Download Paid Version" msgstr "" #: templates/account.php:713 msgid "Sites" msgstr "" #: templates/account.php:726 msgid "Search by address" msgstr "" #: templates/account.php:735, templates/debug.php:466 msgid "Address" msgstr "" #: templates/account.php:736 msgid "License" msgstr "" #: templates/account.php:737 msgid "Plan" msgstr "" #: templates/account.php:788 msgctxt "as software license" msgid "License" msgstr "" #: templates/account.php:917 msgctxt "verb" msgid "Hide" msgstr "" #: templates/account.php:939, templates/forms/data-debug-mode.php:31, templates/forms/deactivation/form.php:358, templates/forms/deactivation/form.php:389 msgid "Processing" msgstr "" #: templates/account.php:942 msgid "Get updates for bleeding edge Beta versions of %s." msgstr "" #: templates/account.php:1000 msgid "Cancelling %s" msgstr "" #: templates/account.php:1000, templates/account.php:1017, templates/forms/subscription-cancellation.php:27, templates/forms/deactivation/form.php:178 msgid "trial" msgstr "" #: templates/account.php:1015, templates/forms/deactivation/form.php:195 msgid "Cancelling %s..." msgstr "" #: templates/account.php:1018, templates/forms/subscription-cancellation.php:28, templates/forms/deactivation/form.php:179 msgid "subscription" msgstr "" #: templates/account.php:1032 msgid "Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?" msgstr "" #: templates/account.php:1106 msgid "Disabling white-label mode" msgstr "" #: templates/account.php:1107 msgid "Enabling white-label mode" msgstr "" #: templates/add-ons.php:38 msgid "View details" msgstr "" #: templates/add-ons.php:48 msgid "Add Ons for %s" msgstr "" #: templates/add-ons.php:58 msgid "We couldn't load the add-ons list. It's probably an issue on our side, please try to come back in few minutes." msgstr "" #: templates/add-ons.php:229 msgctxt "active add-on" msgid "Active" msgstr "" #: templates/add-ons.php:230 msgctxt "installed add-on" msgid "Installed" msgstr "" #: templates/admin-notice.php:17, templates/forms/license-activation.php:245, templates/forms/resend-key.php:80 msgctxt "as close a window" msgid "Dismiss" msgstr "" #. translators: %s: Number of seconds #: templates/auto-installation.php:45 msgid "%s sec" msgstr "" #: templates/auto-installation.php:83 msgid "Automatic Installation" msgstr "" #: templates/auto-installation.php:93 msgid "An automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now." msgstr "" #: templates/auto-installation.php:104 msgid "The installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page." msgstr "" #: templates/auto-installation.php:109 msgid "Cancel Installation" msgstr "" #. translators: %s: name (e.g. Hey John,) #: templates/connect.php:118 msgctxt "greeting" msgid "Hey %s," msgstr "" #. translators: %1$s: plugin name (e.g., "Awesome Plugin"); %2$s: version (e.g., "1.2.3") #: templates/connect.php:185 msgid "Thank you for updating to %1$s v%2$s!" msgstr "" #: templates/connect.php:177 msgid "Never miss an important update" msgstr "" #: templates/connect.php:195 msgid "Allow & Continue" msgstr "" #. translators: %s: module type (plugin, theme, or add-on) #: templates/connect.php:235 msgid "We have introduced this opt-in so you never miss an important update and help us make the %s more compatible with your site and better at doing what you need it to." msgstr "" #: templates/connect.php:237 msgid "Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info." msgstr "" #: templates/connect.php:240 msgid "If you skip this, that's okay! %1$s will still work just fine." msgstr "" #: templates/connect.php:226 msgid "Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info. This will help us make the %s more compatible with your site and better at doing what you need it to." msgstr "" #: templates/connect.php:215 msgid "Welcome to %s! To get started, please enter your license key:" msgstr "" #: templates/connect.php:199 msgid "Re-send activation email" msgstr "" #: templates/connect.php:203 msgid "Thanks %s!" msgstr "" #: templates/connect.php:204 msgid "You should receive a confirmation email for %s to your mailbox at %s. Please make sure you click the button in that email to %s." msgstr "" #: templates/connect.php:270 msgid "We're excited to introduce the Freemius network-level integration." msgstr "" #: templates/connect.php:284 msgid "During the update process we detected %s site(s) in the network that are still pending your attention." msgstr "" #: templates/connect.php:273 msgid "During the update process we detected %d site(s) that are still pending license activation." msgstr "" #: templates/connect.php:275 msgid "If you'd like to use the %s on those sites, please enter your license key below and click the activation button." msgstr "" #: templates/connect.php:277 msgid "%s's paid features" msgstr "" #: templates/connect.php:282 msgid "Alternatively, you can skip it for now and activate the license later, in your %s's network-level Account page." msgstr "" #: templates/connect.php:293, templates/forms/data-debug-mode.php:35, templates/forms/license-activation.php:42 msgid "License key" msgstr "" #: templates/connect.php:296, templates/forms/license-activation.php:22 msgid "Can't find your license key?" msgstr "" #: templates/connect.php:359, templates/connect.php:689, templates/forms/deactivation/retry-skip.php:20 msgctxt "verb" msgid "Skip" msgstr "" #: templates/connect.php:362 msgid "Delegate to Site Admins" msgstr "" #: templates/connect.php:362 msgid "If you click it, this decision will be delegated to the sites administrators." msgstr "" #: templates/connect.php:391 msgid "License issues?" msgstr "" #: templates/connect.php:420 msgid "This will allow %s to" msgstr "" #: templates/connect.php:415 msgid "For delivery of security & feature updates, and license management, %s needs to" msgstr "" #: templates/connect.php:438 msgid "Have a license key?" msgstr "" #: templates/connect.php:435 msgid "Don't have a license key?" msgstr "" #: templates/connect.php:446 msgid "Freemius is our licensing and software updates engine" msgstr "" #: templates/connect.php:449 msgid "Privacy Policy" msgstr "" #: templates/connect.php:454 msgid "Terms of Service" msgstr "" #: templates/connect.php:452 msgid "License Agreement" msgstr "" #: templates/connect.php:875 msgctxt "as in the process of sending an email" msgid "Sending email" msgstr "" #: templates/connect.php:876 msgctxt "as activating plugin" msgid "Activating" msgstr "" #: templates/contact.php:63 msgid "Contact" msgstr "" #: templates/debug.php:17 msgctxt "as turned off" msgid "Off" msgstr "" #: templates/debug.php:18 msgctxt "as turned on" msgid "On" msgstr "" #: templates/debug.php:29, includes/managers/class-fs-debug-manager.php:26 msgid "Freemius Debug" msgstr "" #: templates/debug.php:29 msgid "SDK" msgstr "" #: templates/debug.php:32 msgctxt "as code debugging" msgid "Debugging" msgstr "" #: templates/debug.php:38 msgctxt "timer for auto-disabling debug" msgid "Auto off in:" msgstr "" #: templates/debug.php:117, templates/debug.php:338, templates/debug.php:474, templates/debug.php:639 msgid "Actions" msgstr "" #: templates/debug.php:127 msgid "Are you sure you want to delete all Freemius data?" msgstr "" #: templates/debug.php:127 msgid "Delete All Accounts" msgstr "" #: templates/debug.php:134 msgid "Clear API Cache" msgstr "" #: templates/debug.php:142 msgid "Clear Updates Transients" msgstr "" #: templates/debug.php:151 msgid "Reset Deactivation Snoozing" msgstr "" #: templates/debug.php:159 msgid "Sync Data From Server" msgstr "" #: templates/debug.php:168 msgid "Migrate Options to Network" msgstr "" #: templates/debug.php:173 msgid "Load DB Option" msgstr "" #: templates/debug.php:176 msgid "Set DB Option" msgstr "" #: templates/debug.php:270 msgid "Key" msgstr "" #: templates/debug.php:271 msgid "Value" msgstr "" #: templates/debug.php:287 msgctxt "as software development kit versions" msgid "SDK Versions" msgstr "" #: templates/debug.php:292 msgid "SDK Path" msgstr "" #: templates/debug.php:293, templates/debug.php:332 msgid "Module Path" msgstr "" #: templates/debug.php:294 msgid "Is Active" msgstr "" #: templates/debug.php:322, templates/debug/plugins-themes-sync.php:35 msgid "Plugins" msgstr "" #: templates/debug.php:322, templates/debug/plugins-themes-sync.php:56 msgid "Themes" msgstr "" #: templates/debug.php:327, templates/debug.php:468, templates/debug.php:576, templates/debug/scheduled-crons.php:80 msgid "Slug" msgstr "" #: templates/debug.php:329, templates/debug.php:575 msgid "Title" msgstr "" #: templates/debug.php:330 msgctxt "as application program interface" msgid "API" msgstr "" #: templates/debug.php:331 msgid "Freemius State" msgstr "" #: templates/debug.php:335 msgid "Network Blog" msgstr "" #: templates/debug.php:336 msgid "Network User" msgstr "" #: templates/debug.php:382 msgctxt "as connection was successful" msgid "Connected" msgstr "" #: templates/debug.php:384 msgctxt "as connection blocked" msgid "Blocked" msgstr "" #: templates/debug.php:385 msgctxt "API connectivity state is unknown" msgid "Unknown" msgstr "" #: templates/debug.php:421 msgid "Simulate Trial Promotion" msgstr "" #: templates/debug.php:433 msgid "Simulate Network Upgrade" msgstr "" #. translators: %s: 'plugin' or 'theme' #: templates/debug.php:457 msgid "%s Installs" msgstr "" #: templates/debug.php:459 msgctxt "like websites" msgid "Sites" msgstr "" #: templates/debug.php:465, templates/account/partials/site.php:156 msgid "Blog ID" msgstr "" #: templates/debug.php:470 msgid "License ID" msgstr "" #: templates/debug.php:556, templates/debug.php:662, templates/account/partials/addon.php:440 msgctxt "verb" msgid "Delete" msgstr "" #: templates/debug.php:570 msgid "Add Ons of module %s" msgstr "" #: templates/debug.php:629 msgid "Users" msgstr "" #: templates/debug.php:636 msgid "Verified" msgstr "" #: templates/debug.php:678 msgid "%s Licenses" msgstr "" #: templates/debug.php:683 msgid "Plugin ID" msgstr "" #: templates/debug.php:685 msgid "Plan ID" msgstr "" #: templates/debug.php:686 msgid "Quota" msgstr "" #: templates/debug.php:687 msgid "Activated" msgstr "" #: templates/debug.php:688 msgid "Blocking" msgstr "" #: templates/debug.php:689, templates/debug.php:760, templates/debug/logger.php:22 msgid "Type" msgstr "" #: templates/debug.php:691 msgctxt "as expiration date" msgid "Expiration" msgstr "" #: templates/debug.php:719 msgid "Debug Log" msgstr "" #: templates/debug.php:723 msgid "All Types" msgstr "" #: templates/debug.php:730 msgid "All Requests" msgstr "" #: templates/debug.php:735, templates/debug.php:764, templates/debug/logger.php:25 msgid "File" msgstr "" #: templates/debug.php:736, templates/debug.php:762, templates/debug/logger.php:23 msgid "Function" msgstr "" #: templates/debug.php:737 msgid "Process ID" msgstr "" #: templates/debug.php:738 msgid "Logger" msgstr "" #: templates/debug.php:739, templates/debug.php:763, templates/debug/logger.php:24 msgid "Message" msgstr "" #: templates/debug.php:741 msgid "Filter" msgstr "" #: templates/debug.php:749 msgid "Download" msgstr "" #: templates/debug.php:765, templates/debug/logger.php:26 msgid "Timestamp" msgstr "" #. translators: %s: Page name #: templates/secure-https-header.php:28 msgid "Secure HTTPS %s page, running from an external domain" msgstr "" #: includes/customizer/class-fs-customizer-support-section.php:55, templates/plugin-info/features.php:43 msgid "Support" msgstr "" #: includes/debug/class-fs-debug-bar-panel.php:51, templates/debug/api-calls.php:54, templates/debug/logger.php:62 msgctxt "milliseconds" msgid "ms" msgstr "" #: includes/debug/debug-bar-start.php:41 msgid "Freemius API" msgstr "" #: includes/debug/debug-bar-start.php:42 msgid "Requests" msgstr "" #: includes/managers/class-fs-clone-manager.php:839 msgid "Invalid clone resolution action." msgstr "" #: includes/managers/class-fs-clone-manager.php:1024 msgid "products" msgstr "" #: includes/managers/class-fs-clone-manager.php:1211 msgid "The products below have been placed into safe mode because we noticed that %2$s is an exact copy of %3$s:%1$s" msgstr "" #: includes/managers/class-fs-clone-manager.php:1212 msgid "The products below have been placed into safe mode because we noticed that %2$s is an exact copy of these sites:%3$s%1$s" msgstr "" #: includes/managers/class-fs-clone-manager.php:1205 msgid "%1$s has been placed into safe mode because we noticed that %2$s is an exact copy of %3$s." msgstr "" #: includes/managers/class-fs-clone-manager.php:1238 msgid "the above-mentioned sites" msgstr "" #: includes/managers/class-fs-clone-manager.php:1251 msgid "Is %2$s a duplicate of %4$s?" msgstr "" #: includes/managers/class-fs-clone-manager.php:1252 msgid "Yes, %2$s is a duplicate of %4$s for the purpose of testing, staging, or development." msgstr "" #: includes/managers/class-fs-clone-manager.php:1257 msgid "Long-Term Duplicate" msgstr "" #: includes/managers/class-fs-clone-manager.php:1262 msgid "Duplicate Website" msgstr "" #: includes/managers/class-fs-clone-manager.php:1268 msgid "Is %2$s the new home of %4$s?" msgstr "" #: includes/managers/class-fs-clone-manager.php:1270 msgid "Yes, %%2$s is replacing %%4$s. I would like to migrate my %s from %%4$s to %%2$s." msgstr "" #: includes/managers/class-fs-clone-manager.php:1271, templates/forms/subscription-cancellation.php:52 msgid "license" msgstr "" #: includes/managers/class-fs-clone-manager.php:1271 msgid "data" msgstr "" #: includes/managers/class-fs-clone-manager.php:1277 msgid "Migrate License" msgstr "" #: includes/managers/class-fs-clone-manager.php:1278 msgid "Migrate" msgstr "" #: includes/managers/class-fs-clone-manager.php:1284 msgid "Is %2$s a new website?" msgstr "" #: includes/managers/class-fs-clone-manager.php:1285 msgid "Yes, %2$s is a new and different website that is separate from %4$s." msgstr "" #: includes/managers/class-fs-clone-manager.php:1287 msgid "It requires license activation." msgstr "" #: includes/managers/class-fs-clone-manager.php:1294 msgid "New Website" msgstr "" #: includes/managers/class-fs-clone-manager.php:1319 msgctxt "Clone resolution admin notice products list label" msgid "Products" msgstr "" #: includes/managers/class-fs-clone-manager.php:1408 msgid "You marked this website, %s, as a temporary duplicate of %s." msgstr "" #: includes/managers/class-fs-clone-manager.php:1409 msgid "You marked this website, %s, as a temporary duplicate of these sites" msgstr "" #: includes/managers/class-fs-clone-manager.php:1423 msgid "%s automatic security & feature updates and paid functionality will keep working without interruptions until %s (or when your license expires, whatever comes first)." msgstr "" #: includes/managers/class-fs-clone-manager.php:1426 msgctxt "\"The \", e.g.: \"The plugin\"" msgid "The %s's" msgstr "" #: includes/managers/class-fs-clone-manager.php:1429 msgid "The following products'" msgstr "" #: includes/managers/class-fs-clone-manager.php:1437 msgid "If this is a long term duplicate, to keep automatic updates and paid functionality after %s, please %s." msgstr "" #: includes/managers/class-fs-clone-manager.php:1439 msgid "activate a license here" msgstr "" #: includes/managers/class-fs-permission-manager.php:191 msgid "View Basic Website Info" msgstr "" #: includes/managers/class-fs-permission-manager.php:192 msgid "Homepage URL & title, WP & PHP versions, and site language" msgstr "" #. translators: %s: 'Plugin' or 'Theme' #: includes/managers/class-fs-permission-manager.php:195 msgid "To provide additional functionality that's relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the %s should be translated and tailored to." msgstr "" #: includes/managers/class-fs-permission-manager.php:207 msgid "View Basic %s Info" msgstr "" #. translators: %s: 'Plugin' or 'Theme' #: includes/managers/class-fs-permission-manager.php:210 msgid "Current %s & SDK versions, and if active or uninstalled" msgstr "" #: includes/managers/class-fs-permission-manager.php:261 msgid "View License Essentials" msgstr "" #. translators: %s: 'Plugin' or 'Theme' #: includes/managers/class-fs-permission-manager.php:272 msgid "To let you manage & control where the license is activated and ensure %s security & feature updates are only delivered to websites you authorize." msgstr "" #: includes/managers/class-fs-permission-manager.php:284 msgid "View %s State" msgstr "" #. translators: %s: 'Plugin' or 'Theme' #: includes/managers/class-fs-permission-manager.php:287 msgid "Is active, deactivated, or uninstalled" msgstr "" #: includes/managers/class-fs-permission-manager.php:290 msgid "So you can reuse the license when the %s is no longer active." msgstr "" #: includes/managers/class-fs-permission-manager.php:326 msgid "View Diagnostic Info" msgstr "" #: includes/managers/class-fs-permission-manager.php:326, includes/managers/class-fs-permission-manager.php:363 msgid "optional" msgstr "" #: includes/managers/class-fs-permission-manager.php:327 msgid "WordPress & PHP versions, site language & title" msgstr "" #. translators: %s: 'Plugin' or 'Theme' #: includes/managers/class-fs-permission-manager.php:330 msgid "To avoid breaking your website due to WordPress or PHP version incompatibilities, and recognize which languages & regions the %s should be translated and tailored to." msgstr "" #: includes/managers/class-fs-permission-manager.php:363 msgid "View Plugins & Themes List" msgstr "" #: includes/managers/class-fs-permission-manager.php:364 msgid "Names, slugs, versions, and if active or not" msgstr "" #: includes/managers/class-fs-permission-manager.php:365 msgid "To ensure compatibility and avoid conflicts with your installed plugins and themes." msgstr "" #: includes/managers/class-fs-permission-manager.php:382 msgid "View Basic Profile Info" msgstr "" #: includes/managers/class-fs-permission-manager.php:383 msgid "Your WordPress user's: first & last name, and email address" msgstr "" #: includes/managers/class-fs-permission-manager.php:384 msgid "Never miss important updates, get security warnings before they become public knowledge, and receive notifications about special offers and awesome new features." msgstr "" #: includes/managers/class-fs-permission-manager.php:405 msgid "Newsletter" msgstr "" #: includes/managers/class-fs-permission-manager.php:406 msgid "Updates, announcements, marketing, no spam" msgstr "" #: templates/account/billing.php:22 msgctxt "verb" msgid "Update" msgstr "" #: templates/account/billing.php:33 msgid "Billing" msgstr "" #: templates/account/billing.php:38, templates/account/billing.php:38 msgid "Business name" msgstr "" #: templates/account/billing.php:39, templates/account/billing.php:39 msgid "Tax / VAT ID" msgstr "" #: templates/account/billing.php:42, templates/account/billing.php:42, templates/account/billing.php:43, templates/account/billing.php:43 msgid "Address Line %d" msgstr "" #: templates/account/billing.php:46, templates/account/billing.php:46 msgid "City" msgstr "" #: templates/account/billing.php:46, templates/account/billing.php:46 msgid "Town" msgstr "" #: templates/account/billing.php:47, templates/account/billing.php:47 msgid "ZIP / Postal Code" msgstr "" #: templates/account/billing.php:302 msgid "Country" msgstr "" #: templates/account/billing.php:304 msgid "Select Country" msgstr "" #: templates/account/billing.php:311, templates/account/billing.php:312 msgid "State" msgstr "" #: templates/account/billing.php:311, templates/account/billing.php:312 msgid "Province" msgstr "" #: templates/account/payments.php:29 msgid "Payments" msgstr "" #: templates/account/payments.php:36 msgid "Date" msgstr "" #: templates/account/payments.php:37 msgid "Amount" msgstr "" #: templates/account/payments.php:38, templates/account/payments.php:50 msgid "Invoice" msgstr "" #: templates/checkout/frame.php:77 msgid "Checkout" msgstr "" #: templates/checkout/frame.php:77 msgid "PCI compliant" msgstr "" #: templates/checkout/process-redirect.php:41 msgid "Processing, please wait and do not close or refresh this window..." msgstr "" #: templates/checkout/redirect.php:87 msgid "Redirecting, please click here if you're stuck..." msgstr "" #: templates/connect/permissions-group.php:31, templates/forms/optout.php:26, templates/js/permissions.php:78 msgctxt "verb" msgid "Opt Out" msgstr "" #: templates/connect/permissions-group.php:32, templates/js/permissions.php:77 msgctxt "verb" msgid "Opt In" msgstr "" #: templates/debug/api-calls.php:56 msgid "API" msgstr "" #: templates/debug/api-calls.php:68 msgid "Method" msgstr "" #: templates/debug/api-calls.php:69 msgid "Code" msgstr "" #: templates/debug/api-calls.php:70 msgid "Length" msgstr "" #: templates/debug/api-calls.php:71 msgctxt "as file/folder path" msgid "Path" msgstr "" #: templates/debug/api-calls.php:73 msgid "Body" msgstr "" #: templates/debug/api-calls.php:75 msgid "Result" msgstr "" #: templates/debug/api-calls.php:76 msgid "Start" msgstr "" #: templates/debug/api-calls.php:77 msgid "End" msgstr "" #: templates/debug/logger.php:15 msgid "Log" msgstr "" #. translators: %s: time period (e.g. In "2 hours") #: templates/debug/plugins-themes-sync.php:18, templates/debug/scheduled-crons.php:91 msgid "In %s" msgstr "" #. translators: %s: time period (e.g. "2 hours" ago) #: templates/debug/plugins-themes-sync.php:20, templates/debug/scheduled-crons.php:93 msgid "%s ago" msgstr "" #: templates/debug/plugins-themes-sync.php:21, templates/debug/scheduled-crons.php:74 msgctxt "seconds" msgid "sec" msgstr "" #: templates/debug/plugins-themes-sync.php:23 msgid "Plugins & Themes Sync" msgstr "" #: templates/debug/plugins-themes-sync.php:28 msgid "Total" msgstr "" #: templates/debug/plugins-themes-sync.php:29, templates/debug/scheduled-crons.php:84 msgid "Last" msgstr "" #: templates/debug/scheduled-crons.php:76 msgid "Scheduled Crons" msgstr "" #: templates/debug/scheduled-crons.php:81 msgid "Module" msgstr "" #: templates/debug/scheduled-crons.php:82 msgid "Module Type" msgstr "" #: templates/debug/scheduled-crons.php:83 msgid "Cron Type" msgstr "" #: templates/debug/scheduled-crons.php:85 msgid "Next" msgstr "" #: templates/forms/affiliation.php:86 msgid "Non-expiring" msgstr "" #: templates/forms/affiliation.php:89 msgid "Apply to become an affiliate" msgstr "" #: templates/forms/affiliation.php:137 msgid "Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support." msgstr "" #: templates/forms/affiliation.php:134 msgid "Thank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days." msgstr "" #: templates/forms/affiliation.php:131 msgid "Your affiliation account was temporarily suspended." msgstr "" #: templates/forms/affiliation.php:128 msgid "Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information." msgstr "" #: templates/forms/affiliation.php:113 msgid "Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s." msgstr "" #: templates/forms/affiliation.php:150 msgid "Like the %s? Become our ambassador and earn cash ;-)" msgstr "" #: templates/forms/affiliation.php:151 msgid "Refer new customers to our %s and earn %s commission on each successful sale you refer!" msgstr "" #: templates/forms/affiliation.php:154 msgid "Program Summary" msgstr "" #: templates/forms/affiliation.php:156 msgid "%s commission when a customer purchases a new license." msgstr "" #: templates/forms/affiliation.php:158 msgid "Get commission for automated subscription renewals." msgstr "" #: templates/forms/affiliation.php:161 msgid "%s tracking cookie after the first visit to maximize earnings potential." msgstr "" #: templates/forms/affiliation.php:164 msgid "Unlimited commissions." msgstr "" #: templates/forms/affiliation.php:166 msgid "%s minimum payout amount." msgstr "" #: templates/forms/affiliation.php:167 msgid "Payouts are in USD and processed monthly via PayPal." msgstr "" #: templates/forms/affiliation.php:168 msgid "As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days." msgstr "" #: templates/forms/affiliation.php:171 msgid "Affiliate" msgstr "" #: templates/forms/affiliation.php:174, templates/forms/resend-key.php:23 msgid "Email address" msgstr "" #: templates/forms/affiliation.php:178 msgid "Full name" msgstr "" #: templates/forms/affiliation.php:182 msgid "PayPal account email address" msgstr "" #: templates/forms/affiliation.php:186 msgid "Where are you going to promote the %s?" msgstr "" #: templates/forms/affiliation.php:188 msgid "Enter the domain of your website or other websites from where you plan to promote the %s." msgstr "" #: templates/forms/affiliation.php:190 msgid "Add another domain" msgstr "" #: templates/forms/affiliation.php:194 msgid "Extra Domains" msgstr "" #: templates/forms/affiliation.php:195 msgid "Extra domains where you will be marketing the product from." msgstr "" #: templates/forms/affiliation.php:205 msgid "Promotion methods" msgstr "" #: templates/forms/affiliation.php:208 msgid "Social media (Facebook, Twitter, etc.)" msgstr "" #: templates/forms/affiliation.php:212 msgid "Mobile apps" msgstr "" #: templates/forms/affiliation.php:216 msgid "Website, email, and social media statistics (optional)" msgstr "" #: templates/forms/affiliation.php:219 msgid "Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential)." msgstr "" #: templates/forms/affiliation.php:223 msgid "How will you promote us?" msgstr "" #: templates/forms/affiliation.php:226 msgid "Please provide details on how you intend to promote %s (please be as specific as possible)." msgstr "" #: templates/forms/affiliation.php:238, templates/forms/resend-key.php:22, templates/forms/subscription-cancellation.php:142, templates/account/partials/disconnect-button.php:92 msgid "Cancel" msgstr "" #: templates/forms/affiliation.php:240 msgid "Become an affiliate" msgstr "" #: templates/forms/data-debug-mode.php:25 msgid "Please enter the license key to enable the debug mode:" msgstr "" #: templates/forms/data-debug-mode.php:27 msgid "To enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your \"My Profile\" section of your User Dashboard:" msgstr "" #: templates/forms/data-debug-mode.php:32 msgid "Submit" msgstr "" #: templates/forms/data-debug-mode.php:36 msgid "User key" msgstr "" #: templates/forms/email-address-update.php:32 msgid "Email address update" msgstr "" #: templates/forms/email-address-update.php:33, templates/forms/user-change.php:81 msgctxt "close window" msgid "Dismiss" msgstr "" #: templates/forms/email-address-update.php:38 msgid "Enter the new email address" msgstr "" #: templates/forms/email-address-update.php:42 msgid "Are both %s and %s your email addresses?" msgstr "" #: templates/forms/email-address-update.php:50 msgid "Yes - both addresses are mine" msgstr "" #: templates/forms/email-address-update.php:57 msgid "%s is my client's email address" msgstr "" #: templates/forms/email-address-update.php:66 msgid "%s is my email address" msgstr "" #: templates/forms/email-address-update.php:75 msgid "Would you like to merge %s into %s?" msgstr "" #: templates/forms/email-address-update.php:84 msgid "Yes - move all my data and assets from %s to %s" msgstr "" #: templates/forms/email-address-update.php:94 msgid "No - only move this site's data to %s" msgstr "" #: templates/forms/email-address-update.php:292, templates/forms/email-address-update.php:298 msgid "Update" msgstr "" #: templates/forms/license-activation.php:23 msgid "Please enter the license key that you received in the email right after the purchase:" msgstr "" #: templates/forms/license-activation.php:28 msgid "Update License" msgstr "" #: templates/forms/license-activation.php:34 msgid "The %1$s will be periodically sending essential license data to %2$s to check for security and feature updates, and verify the validity of your license." msgstr "" #: templates/forms/license-activation.php:39 msgid "Agree & Activate License" msgstr "" #: templates/forms/license-activation.php:206 msgid "Associate with the license owner's account." msgstr "" #: templates/forms/optout.php:104 msgid "Keep automatic updates" msgstr "" #: templates/forms/optout.php:44 msgid "Communication" msgstr "" #: templates/forms/optout.php:56 msgid "Stay Connected" msgstr "" #: templates/forms/optout.php:61 msgid "Diagnostic Info" msgstr "" #: templates/forms/optout.php:77 msgid "Keep Sharing" msgstr "" #: templates/forms/optout.php:82 msgid "Extensions" msgstr "" #: templates/forms/premium-versions-upgrade-handler.php:40 msgid "There is a new version of %s available." msgstr "" #: templates/forms/premium-versions-upgrade-handler.php:41 msgid " %s to access version %s security & feature updates, and support." msgstr "" #: templates/forms/premium-versions-upgrade-handler.php:54 msgid "New Version Available" msgstr "" #: templates/forms/premium-versions-upgrade-handler.php:75 msgctxt "close a window" msgid "Dismiss" msgstr "" #: templates/forms/resend-key.php:21 msgid "Send License Key" msgstr "" #: templates/forms/resend-key.php:58 msgid "Enter the email address you've used during the purchase and we will resend you the license key." msgstr "" #: templates/forms/resend-key.php:59 msgid "Enter the email address you've used for the upgrade below and we will resend you the license key." msgstr "" #: templates/forms/subscription-cancellation.php:37 msgid "Deactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site." msgstr "" #: templates/forms/subscription-cancellation.php:47 msgid "In case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?" msgstr "" #: templates/forms/subscription-cancellation.php:57 msgid "Cancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site." msgstr "" #: templates/forms/subscription-cancellation.php:68 msgid "Don't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support." msgstr "" #: templates/forms/subscription-cancellation.php:103 msgid "Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license." msgstr "" #: templates/forms/subscription-cancellation.php:136 msgid "Cancel %s?" msgstr "" #: templates/forms/subscription-cancellation.php:143 msgid "Proceed" msgstr "" #: templates/forms/subscription-cancellation.php:191, templates/forms/deactivation/form.php:216 msgid "Cancel %s & Proceed" msgstr "" #. translators: %1$s: Number of trial days; %2$s: Plan name; #: templates/forms/trial-start.php:22 msgid "You are 1-click away from starting your %1$s-day free trial of the %2$s plan." msgstr "" #. translators: %s: Link to freemius.com #: templates/forms/trial-start.php:28 msgid "For compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial." msgstr "" #: templates/forms/user-change.php:26 msgid "By changing the user, you agree to transfer the account ownership to:" msgstr "" #: templates/forms/user-change.php:28 msgid "I Agree - Change User" msgstr "" #: templates/forms/user-change.php:30 msgid "Enter email address" msgstr "" #: templates/js/permissions.php:337, templates/js/permissions.php:485 msgid "Saved" msgstr "" #: templates/js/style-premium-theme.php:39 msgid "Premium" msgstr "" #: templates/js/style-premium-theme.php:42 msgid "Beta" msgstr "" #: templates/partials/network-activation.php:36 msgid "Activate license on all pending sites." msgstr "" #: templates/partials/network-activation.php:37 msgid "Apply on all pending sites." msgstr "" #: templates/partials/network-activation.php:32 msgid "Activate license on all sites in the network." msgstr "" #: templates/partials/network-activation.php:33 msgid "Apply on all sites in the network." msgstr "" #: templates/partials/network-activation.php:45, templates/partials/network-activation.php:79 msgid "allow" msgstr "" #: templates/partials/network-activation.php:48, templates/partials/network-activation.php:82 msgid "delegate" msgstr "" #: templates/partials/network-activation.php:52, templates/partials/network-activation.php:86 msgid "skip" msgstr "" #: templates/plugin-info/description.php:67, templates/plugin-info/screenshots.php:26 msgid "Click to view full-size screenshot %d" msgstr "" #: templates/plugin-info/features.php:56 msgid "Unlimited Updates" msgstr "" #: templates/account/partials/activate-license-button.php:46 msgid "Localhost" msgstr "" #: templates/account/partials/activate-license-button.php:50 msgctxt "as 5 licenses left" msgid "%s left" msgstr "" #: templates/account/partials/activate-license-button.php:51 msgid "Last license" msgstr "" #: templates/account/partials/addon.php:200 msgid "No expiration" msgstr "" #: templates/account/partials/addon.php:190 msgid "Cancelled" msgstr "" #. translators: %s is replaced with the website's homepage address. #: templates/account/partials/disconnect-button.php:78 msgid "Disconnecting the website will permanently remove %s from your User Dashboard's account." msgstr "" #: templates/account/partials/disconnect-button.php:74 msgid "By disconnecting the website, previously shared diagnostic data about %1$s will be deleted and no longer visible to %2$s." msgstr "" #. translators: %1$s is replaced by the paid plan name, %2$s is replaced with an anchor link with the text "User Dashboard". #: templates/account/partials/disconnect-button.php:84 msgid "If you wish to cancel your %1$s plan's subscription instead, please navigate to the %2$s and cancel it there." msgstr "" #: templates/account/partials/disconnect-button.php:88 msgid "Are you sure you would like to proceed with the disconnection?" msgstr "" #: templates/account/partials/site.php:190 msgid "Owner Name" msgstr "" #: templates/account/partials/site.php:202 msgid "Owner Email" msgstr "" #: templates/account/partials/site.php:214 msgid "Owner ID" msgstr "" #: templates/account/partials/site.php:288 msgid "Subscription" msgstr "" #: templates/forms/deactivation/contact.php:19 msgid "Sorry for the inconvenience and we are here to help if you give us a chance." msgstr "" #: templates/forms/deactivation/contact.php:22 msgid "Contact Support" msgstr "" #: templates/forms/deactivation/form.php:65 msgid "Anonymous feedback" msgstr "" #: templates/forms/deactivation/form.php:71 msgid "hour" msgstr "" #: templates/forms/deactivation/form.php:76 msgid "hours" msgstr "" #: templates/forms/deactivation/form.php:81, templates/forms/deactivation/form.php:86 msgid "days" msgstr "" #: templates/forms/deactivation/form.php:106 msgid "Deactivate" msgstr "" #: templates/forms/deactivation/form.php:108 msgid "Activate %s" msgstr "" #: templates/forms/deactivation/form.php:111 msgid "Submit & %s" msgstr "" #: templates/forms/deactivation/form.php:130 msgid "Quick Feedback" msgstr "" #: templates/forms/deactivation/form.php:134 msgid "If you have a moment, please let us know why you are %s" msgstr "" #: templates/forms/deactivation/form.php:134 msgid "deactivating" msgstr "" #: templates/forms/deactivation/form.php:134 msgid "switching" msgstr "" #: templates/forms/deactivation/form.php:448 msgid "Kindly tell us the reason so we can improve." msgstr "" #: templates/forms/deactivation/form.php:478 msgid "Snooze & %s" msgstr "" #: templates/forms/deactivation/form.php:638 msgid "Yes - %s" msgstr "" #: templates/forms/deactivation/form.php:645 msgid "Skip & %s" msgstr "" #: templates/forms/deactivation/retry-skip.php:21 msgid "Click here to use the plugin anonymously" msgstr "" #: templates/forms/deactivation/retry-skip.php:23 msgid "You might have missed it, but you don't have to share any data and can just %s the opt-in." msgstr "" freemius/languages/freemius-nl_NL.mo000064400000130362147600046700013516 0ustar00T A S %#! I! U!a!h!6{!!_g"#"" # # #'#.#6#?#G#@P#H##O#=$A$`$$$$ $$$$$&$-!%O% d%n%}%%%%5%%% & &'&@& Y& f&p&o&&&''"''2(!9(a[(0(())&).)B)J)S)[) `)n) ))))) W*b*v* * * ***Y*6+E+ V+b+k+p++(+1+%+:,U,Z,k,s, , ,,, ,, ,,x,_- - . .#.7.t?.....//6/ R/]/[/fQ00 00Y0a*1111 1 1;12223 "3 -3 :3G3jV33 33334~+4U45555)P5-z55S56'(6P6MS676g6pA777y7L8e8 8888 88 819.29Oa99A1:ys::a ;o;Bs;,;; ; ;< < 9<D<K<S< e< q<}<<4<< <<<<<= =+= 2= >=J=d= i= v===!== ====%=+>G> _> m>/z>>m>~????? ?? ? ?)?$@A@4J@@5@(@@@-A0AUDAA1cB~BC[1DDDD DD(D*E"0E1SE+E*E&ENFRFZFpF.xF)FFFG GG G+G4GDGVG _GjG{GGGWG HH&H/HJHQHUH^HfH vHH H5HsHlII&III I J$J=JQJYJuJ {JJ&JLJfJeK kKwKK K KK KK KKKL/MNM)nM M M\M N N3NCRNNNNdRO-OO OOO'PMFPGP PPPPPPEQHQ[QmQQQQ*QQ*Q^RfRnRtRdzRR RR SS.Si6SWS"SBT6^TT TT-TTU&+URUlUpU$yUMUU/U.VoNV>VV&WZ;WTWRW.>X.mX9XZX31Y<eYbYPZUVZ_Z [K[([G\#d\)\$\U\)-]W]i]];]6]>^M^S^n^^$^^^^_&9_*`_7____3`E`Z`p```*`1`a2a#Fajaaa aaaa b bNbebbbbbb1b!c)c=c Mc Yc fc qc~c cCccQc=d Md Zddddd dd d d d d d d d e e e(eFiBj$\jj j jj6jkqk)Ull lllll ll lGlM>mmJmmmmnnn 'n3nDnZnqn6n/nn n oo7o Oo [o:ioo o o o"oo p p%pn:pppTq,lq%q.q7q#&r{Jr?rss6sMsVs js vsss ss s&ssst tttt u u u8uuQuuu u uvvv,9v5fv?v vvvvww $w0wGw LwVw \wgw{ww x xxxxyyyyyy$y!z ;zFza{^f{{ {{j{~I||$||| }7}S}[}!b}~~ ~ ~~{~EUd>lfKЀ'3?DY1CZFjh |u z" Մ'(P X;b>`݅>Aۆ ]ׇ5=l-5G%X#~ щ B X fpt{  Ȋӊ */#Z ~/7ϋ/4?ty ƍ ҍ 3 ;EFB&Ԏ#8'`YvЏ8_#*2 CNb r “Nʓ!"D+L.xӔ ۔  /9JYoum -7 Q[_ gr 8}3 =Ldvؘ ޘ&Sxl  ' 2 @M mxBH*78b "5X jZ\(/ 5B J+kPA *6<C JWX\ʠݠ  -I(Qz  } *;Ud ~d^6OF8ͣ *'8`zͤФ(ؤbd!s(5V#WȦ_ a-14B\w,Ԩ9k;`j`sԪRn%E -'N!vV%%. G hu~  ­ έح #'8 < FPY ^ jv  ˮ ծ ߮ UKSdjns{ ȯL̯ #' = HV v °ǰ̰ հ߰ %s to access version %s security & feature updates, and support.%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is the new owner of the account.%s minimum payout amount.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s to access version %s security & feature updates, and support.%s tracking cookie after the first visit to maximize earnings potential.%s's paid features%sClick here%s to choose the sites where you'd like to activate the license on.APIASCII arrow left icon←ASCII arrow right icon➤Account DetailsActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre you sure you want to delete all Freemius data?Are you sure you want to proceed?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBillingBlockingBlog IDBodyBusiness nameBuy a license nowBuy licenseCan't find your license key?CancelCancel %s & ProceedCancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site.Cancel %s?Cancel InstallationCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanCheckoutCityClear API CacheClear Updates TransientsClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site.Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDelegate to Site AdminsDelete All AccountsDetailsDon't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support.Don't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload the latest %s versionDownload the latest versionDownloadedDue to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.During the update process we detected %d site(s) that are still pending license activation.During the update process we detected %s site(s) in the network that are still pending your attention.EmailEmail addressEndEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used for the upgrade below and we will resend you the license key.ErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.FreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFull nameFunctionGet commission for automated subscription renewals.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf you click it, this decision will be delegated to the sites administrators.If you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.Important Upgrade Notice:In %sIn case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?Install Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid module ID.Invalid site details collection.InvoiceIs ActiveIt looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's not what I was looking forJust letting you know that the add-ons information of %s is being pulled from an external server.KeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense KeyLicense keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerMessageMethodMigrate Options to NetworkMobile appsModuleModule PathModule TypeMore information about %sNameNetwork BlogNetwork UserNewNew Version AvailableNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license.Opt InOpt OutOpt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.PayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.Please provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProceedProcess IDProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicenseQuick FeedbackQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRenew your license nowRequestsRequires WordPress VersionResultSDKSDK PathSave %sScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes.Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSimulate Network UpgradeSimulate Trial PromotionSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSocial media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart TrialStart my free %sStateSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a %s of %s available.There is a new version of %s available.This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.TimestampTitleTotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser IDUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress.org Plugin PageYesYes - %sYou already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.You do not have a valid license to access the premium version.You have a %s license.You have successfully updated your %s.You might have missed it, but you don't have to share any data and can just %s the opt-in.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your subscription was successfully cancelled. Your %s plan license will expire in %s.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onactive add-onActiveaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,interjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew versionnot verifiednounPricenounPricingproduct versionVersionsecondssecsend me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Leo Fajardo , 2022 Language-Team: Dutch (Netherlands) (http://app.transifex.com/freemius/wordpress-sdk/language/nl_NL/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: nl_NL Plural-Forms: nplurals=2; plural=(n != 1); X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 %svoor toegang to versie %s beveiliging & features updates en support.Voltooi "%s" Activatie Nu%s Add-on werd succesvol aangekocht.%s Installaties%s Licenties%s geleden%sen bijbehorende uitbreidingen%s commissie als een klant een nieuwe licentie koopt. %s gratis proefperiode werd succesvol stop gezet. Daar de add-on alleen als premium versie beschikbaar is werd deze automatisch gedeactiveerd. Als u de add-on in de toekomst wilt gebruiken dient u een licentie aan te schaffen.%s is uitsluitend beschikbaar als een premium add-on. Je moet een licentie kopen voordat je de plug-in activeert.%s is de nieuwe eigenaar van het account.%s minimum uitbetalingsbedrag.%s of hoger%s beoordeling%s beoordelingen%s sec%s ster%s sterren%s tijd%s tijden%svoor toegang tot versie %s beveiliging en feature updates en support.%s tracking cookie na eerste bezoek om je verdienpotentieel te maximaliseren.%s betaalde mogelijkheden%sKlik hier%s om de sites te kiezen waar op je de licentie wilt activeren.API←➤AccountgegevensActiesActiveerActiveer %sActiveer %s PlanActiveer %s features.Activeer Gratis VersieActiveer LicentieActiveer licentie op alle in behandeling zijnde sites.Activeer licentie op alle sites in het netwerk.Activeer deze add-onGeactiveerdAdd-ons voor %sUitbreidingen van module %sVoeg nog een domein toeUitbreidingUitbreidingenAdd-on moet op WordPress.org of Freemius geplaatst worden.AdresAdresregel %dAffiliateAffiliatieNa uw gratis %s, betaal slechts %sAkkoord & Activeer LicentieAlle RequestsAlle TypesToestaan & Ga VerderJe kunt dat eventueel ook nu overslaan en de licentie later in je %s netwerk-niveau Account pagina activeren. BedragEen geautomatiseerde download en installatie van %s (betaalde versie) van %s zal starten binnen %s. Als je dit handmatig wil doen, klik dan nu op de annuleer knop.Anonieme terugkoppelingPas toe op alle in behandeling zijnde sites.Pas toe op alle sites in het netwerk.Meld je aan om een affiliate partner te wordenWeet u zeker dat u alle Freemius data wilt verwijderen?Weet je zeker dat je wilt doorgaan?Omdat wij 30 dagen reserveren voor eventuele terugstortingen, betalen we alleen commissies uit die ouder dan 30 dagen zijn.Automatische installatie werkt alleen voor opted-in gebruikers.Auto hernieuwd over %sAutomatische InstallatieGemiddelde BeoordelingGeweldigWordt een affiliateFactureringGeblokkeerdBlog IDBodyBedrijfsnaamKoop nu een licentieKoop licentieKan je je licentiesleutel niet vinden?AnnuleerAnnuleer %s & Ga DoorAnnuleer %s - Ik heb niet meer enige beveiligings- en uitbreidingsupdates of ondersteuning voor %s nodig, omdat ik niet van plan ben de %sop deze of enige andere site te gebruiken.%s annuleren?Annuleer InstallatieAbonnement OpzeggenProefperiode OpzeggenGeannuleerdAnnuleren %s%s wordt geannuleerd...Het abonnement annulerenHet stopzetten van de proefperiode zal de toegang tot de premium features onmiddellijk blokkeren. Weet je dat zeker?Verander LicentieEigendom OverdragenWijzig PlanAfrekenenStadAPI-Cache LeegmakenUpdates Transients OpschonenKlik hier om de plug-in anoniem te gebruikenKlik om reviews te bekijken met een beoordeling van%sKlik voor het op volle-grootte bekijken van schermafbeelding %dProductenCodeCompatible totContactContacteer SupportContacteer OnsMedewerkersKon %s niet activeren.LandCron TypeDatumDeactiveerDeactiveer LicentieHet deactiveren en deïnstalleren van de %s zal de licentie automatisch uitschakelen, die je dan kan gebruiken op een andere site.Deactiveren van je licentie zal alle premium features blokkeren, maar geeft je de mogelijkheid de licentie op een andere site te activeren. Weet je zeker dat je wilt doorgaan?DeactivatieDebug LogDelegeren aan Site BeheerdersVerwijder All AccountsDetailsAnnuleer %s niet - Ik wil nog steeds zowel beveiligings- en uitbreidingsupdates ontvangen als contact kunnen opnemen met Support.Heb je geen licentiesleutel?Doneer aan deze plug-inJe plan naar beneden bijstellenDownloadDownload %s VersieDownload de meeste recente %s versieDownload de meeste recente versieGedownloadAls gevolg van het overtreden van onze affiliate voorwaarden, hebben we besloten je affiliate account tijdelijk te blokkeren. Neem voor eventuele vragen alsjeblieft contact op met support.Tijdens het update proces detecteerden we %dsite(s) waarvoor de licentie nog niet geactiveerd is.Tijdens het update proces detecteerden we %dsite(s) in het netwerk die jouw aandacht vereisen.E-mailE-mailadresEindeVoer de domeinnaam in van je website of andere websites waar vanaf je van plan bent de %ste gaan promoten.Voer hieronder het e-mailadres in dat je gebruikt hebt voor de upgrade en we zullen je jouw licentiesleutel opnieuw toesturen.FoutFoutmelding ontvangen van de server:VerlopenVerloopt over %sExtra DomeinenExtra domeinen vanaf waar je het product gaat promoten.BestandFilterVoordat we de proefperiode kunnen starten, vragen we je, in overeenstemming met de Wordpress.org-richtlijnen, in te stemmen je gebruikers- en niet-sensitieve site informatie door de %s periodiek te laten verzenden naar %s om te controleren op nieuwe versies en je proefversie te valideren.GratisGratis ProefperiodeGratis versieFreemius APIFreemius DebugFreemius SDK kon het hoofdbestand van de plug-in niet vinden. Neem a.j.b. contact op met sdk@freemius.com m.b.t. deze fout.Freemius StatusVolledige naamFunctieKrijg een commissie voor automatische abonnementsverlengingen.Heb je een licentiesleutel?Hey, wist je dat %s een samenwerkingsprogramma heeft? Als je de %s goedvindt, kun je onze ambassadeur worden en wat geld verdienen!Hoe bevalt %s tot dusver? Test al onze %s premium features gedurende een%d-daagse gratis proefperiode.Hoe te uploaden en activeren?Hoe ga je ons promoten?Ik kan er niet langer meer voor betalenIk snapte niet hoe ik het aan het werk kon krijgen.Ik vind het niet prettig om mijn informatie met jullie te delenIk vond een beter %sIk heb mijn account geüpgraded maar als ik probeer te Synchroniseren blijft het plan %s.Ik heb de %s niet meer nodig Ik had de %s alleen nodig voor een korte periode.IDAl je er op klikt, zal deze beslissing gedelegeerd worden aan de beheerders van de sites. We zouden het zeer op prijs stellen, als je even hebt, om ons alsjeblieft te laten weten waarom je gaat %sAls je het eigendom van het %s account wilt overdragen aan %s, klik dan op de Eigendom Overdragen knop. Als je de %s op deze sites wil gebruiken, voer dan alsjeblieft de licentiesleutel hieronder in en klik op de activatie-knop.Belangrijke Upgrade Mededeling:Binnen %sMocht je NIET van plan zijn om deze %s te gebruiken op deze site (of op een andere site) - wil je dan het %s ook opzeggen?Installer Gratis Versie NuInstalleer Gratis Versie Update NuInstalleer NuInstalleer Update NuInstalleren van plug-in: %sOngeldige Module-IDOngeldige verzameling van Site Details.FactuurIs ActiefHet lijkt erop dat de licentie niet geactiveerd kon worden.Het lijkt erop dat het deactiveren van je licentie mislukt is.Het lijkt er op dat u niet langer meer in de proefperiode zit, dus er valt niets stop te zetten.Het lijkt erop dat u nog steeds op het %s plan zit. Als u uw plan geüpgraded of veranderd heeft, dan is het waarschijnlijk een fout aan onze kant - sorry.Het lijkt erop dat je site momenteel geen actieve licentie heeft.Het lijkt erop dat een van de authenticatie parameters niet klopt. Update je Publieke Sleutel, Geheime Sleutel & Gebruikers ID en probeer het nogmaals. Het is niet waarna ik opzoek wasVoor alle duidelijkheid, de add-ons informatie van %s wordt opgehaald van een externe server.SleutelWil je alsjeblieft zo vriendelijk zijn om te delen wat niet werkte, zodat we dat kunnen verbeteren voor toekomstige gebruikers ...Wilt je alsjeblieft zo vriendelijk zijn om de reden te vermelden, zodat wij verbeteringen kunnen doorvoeren.LaatsteLaatst GeüpdatetLaatste licentieNieuwste Gratis Versie GeïnstalleerdMeest Recente Versie GeïnstalleerdLees meerLengteLicentieLicentieovereenkomstLicentiesleutelLicentiesleutelLicentiesleutel is leeg.LevenslangVind je de %s goed? Word dan onze ambassadeur en verdien cash ;-)Laad DB-optieLocalhostLogLoggerBerichtMethodesZet Opties over naar NetwerkMobiele appsModuleModule PadModuletypeMeer informatie over %sNaamNetwerk BlogNetwerk GebruikerNieuwNieuwe Versie BeschikbaarNieuwere Gratis Versie (%s) GeïnstalleerdNieuwere Versie (%s) GeïnstalleerdNieuwsbriefVolgendeNeeGeen IDGeen verplichting voor %s - opzeggen kan altijdGeen verplichting voor %s dagen - elk moment opzeggen!Geen creditcard nodigGeen verloopdatumNiet-verlopendeGeen van de %s plannen ondersteunt een proefperiode.OkéAls je licentie verloopt kan je nog steeds gebruik maken van de Gratis versie, maar je zal GEEN toegang meer hebben tot de %sfeatures.Als je licentie afloopt, zul je %s niet meer kunnen gebruiken, tenzij je het opnieuw activeert met een geldige Premium-licentie.Opt InOpt OutOpt-in om "%s" te verbeteren!OverigeE-mail EigenaarID EigenaarNaam EigenaarPCI-comformBetaalde add-on moet op Freemius geplaatst worden.PayPal account e-mailadresBetalingenUitbetalingen zijn in USD en worden maandelijks uitgevoerd via PayPalPlanPlan %s bestaat niet, daarom kan proefperiode niet gestart worden.Plan %s ondersteunt geen proefperiode.Plan IDNeem hier a.u.b. contact met ons opNeem a.u.b. contact met ons op met het volgende bericht:A.u.b. %s downloaden.Voer aalsjeblieft de licentiesleutel in die je ontving in de e-mail direct na de aankoop:Voel je alsjeblieft vrij om elke relevante website of social media statistieken met ons te delen, bijvoorbeeld maandelijkse unieke bezoekers, aantal e-mail abonnees , volgers, etc. (we zullen deze informatie vertrouwelijk houden).Volg alsjeblieft deze stappen om de upgrade te voltooienLaat ons alsjeblieft weten als je op de hoogte gehouden wilt worden van beveiliging & feature updates, educatieve content en zo nu en dan aanbiedingen:Onthou alsjeblieft dat we geen oude prijzen voor verlengingen/nieuwe abonnementen na een annulering kunnen aanhouden. Als je in de toekomst besluit om een abonnement handmatig te vernieuwen, zal de nieuwe prijs (na een prijsverhoging die meestal jaarlijks plaatsvindt) worden berekend.Geef alsjeblieft zo gedetailleerd als mogelijk aan hoe je van plan bent om %s te gaan promoten.Geef alsjeblieft je volledige naam.Plug-inPlug-in HomepagePlug-in IDPlug-in InstallatieWijzigingen LogBeschrijvingVeelgestelde VragenFeatures & PrijzenInstallatieAndere NotitiesReviewsPlug-in is 'Serviceware' wat betekent dat het geen premium code versie bevat. Plug-insSynchronisatie Plug-ins & Thema'sPremiumPremium %s versie is succesvol geactiveerd.Premium add-on versie is reeds geïnstalleerd.Premium versiePremium versie reeds actief.PrijzenPrivacybeleidDoorgaanProces-IDProductenProgramma SamenvattingPromotie methodesProvinciePublieke SleutelLicentie KopenSnelle terugkoppelingQuotaActivatiemail opnieuw versturenVerwijs nieuwe klanten naar onze %s en krijg %s commissie op iedere door jou doorverwezen, geslaagde verkoop!Vernieuw licentieVernieuw je licentie nuAanvragenVereiste WordPress-versieResultaatSDKSDK PadBespaar %sGeplande CronsSchermafbeeldingenZoek op adresGeheime SleutelBeveiligde HTTPS %s pagina, loopt via een extern domeinHet lijkt erop, dat we een tijdelijk probleem hebben met het annuleren van je abonnement. Probeer het alsjeblieft over een paar minuten nog eens.Het lijkt er op dat we een tijdelijk probleem hebben met het opzeggen van uw proefperiode. Probeer het a.u.b. over enkele minuten nog eens.Het lijkt erop dat je de meest recente versie hebt.Selecteer LandVerzend LicentiesleutelActiveer DB-OptieSimuleer Netwerk UpgradeSimuleer Trial ActieEnkele Site LicentieSite IDSite opt-in geslaagd. SitesSla over & %sSlugSocial media (Facebook, Twitter, etc.)Sorry voor het ongemak en we zijn er om je te helpen als je daartoe de kans geeft..Sorry, we konden de e-mail update niet voltooien. Een andere gebruiker met hetzelfde e-mailadres is reeds geregistreerd.StartStart ProefperiodeStart mijn gratis %sStaatVerstuur & %sAbonnementOndersteuningSupportforumSynchroniseer Data Vanaf ServerBtw-nummerServicevoorwaardenBedankt voor je aanvraag voor deelname aan ons affiliate programma, helaas, op dit moment hebben we besloten je aanvraag af te wijzen. Probeer het alsjeblieft over 30 dagen nog eens.Bedankt voor je aanvraag voor deelname aan ons samenwerkingsprogramma. We zullen binnen 14 dagen je gegevens doornemen, waarna we je aanvullende informatie zullen sturen.Hartelijk bedankt voor het gebruik van %s en bijbehorende uitbreidingen!Hartelijk bedankt voor het gebruik van %s!Hartelijk bedankt voor het gebruiken van onze producten!Bedankt!Bedankt %s!Bedankt voor het bevestigen van de eigendomsoverdracht. Zojuist is er een e-mail verstuurd naar %s voor de definitieve goedkeuring. De %s maakte mijn site onbruikbaarDe %s werkte nietDe %s werkte niet zoals verwachtDe %s is uitstekend, maar ik heb een specifieke feature nodig die jullie niet ondersteunenDe %s werkt nietDe %s werkte opeens niet meerHet installatieproces is gestart en kan enkele minuten duren om te voltooien. Wacht alsjeblieft totdat dat gebeurt is - deze pagina niet verversen.Het remote plug-in pakket bevat geen folder met de verwachte slug en hernoemen werkte niet. De upgrade van %s is succesvol voltooid.ThemaThema WisselThema'sEr is een %s van %s beschikbaar.Er is een nieuwe versie van %s beschikbaar.Deze plug-in is niet als compatibel aangemerkt voor je huidige WordPress versie.Deze plug-in is nog niet getest met je huidige WordPress versie. TijdstempelTitelTotaalPlaatsProefperiodeTypeToegang tot het bestandssysteem is niet mogelijk. Bevestig alsjeblieft je inloggegevens.Onbeperkte LicentiesOnbeperkte UpdatesOnbeperkte commissies.Tot %s SitesBijwerkenUpdate LicentieUpdates, aankondigingen, marketing, geen spamUpgradeUpload en activeer de gedownloade versieW00tGebruikers IDGebruikersWaardeVerificatiemail zojuist verstuurd naar %s. Als je deze niet binnen 5 min. hebt ontvangen, kijk dan alsjeblieft in je spambox.GeverifieerdVerifieer E-mailVersie %s is vrijgegeven.Bekijk detailsBekijk betaalde kenmerkenWaarschuwingEr is geen actieve licentie gekoppeld aan dat e-mailadres, ben je zeker dat dat het juiste adres is?We konden je e-mailadres niet vinden in het systeem, ben je zeker dat dat het juiste adres is?We hebben een aantal aanpassingen gedaan op de %s, %s We zijn verheugd om Freemius network-level integratie te introduceren.Website, mail, and social media statistieken (optioneel)Wat had je verwacht?Welke feature?Wat is je %s?Welke bedrag zou je ervoor over hebben?Waar was je naar op zoek?Wat is de naam van het %s?Waar ga je de %s promoten?WordPress.org Plug-in PaginaJaJa - %sU heeft reeds een proefperiode gebruikt.U bent 1-klik verwijderd van het starten van uw %1$s-daagse gratis proefperiode van het %2$s plan.Alles is goed!Je draait de %s al in proefmodus.Je bent slechts een stap verwijderd - %sJe kunt nog steeds van alle %s-mogelijkheden genieten, maar je zult geen toegang hebben tot %s veiligheids- en uitbreidingsupdates, noch ondersteuning.Je hebt geen geldige licentie voor de premium versie.Je hebt een %s licentieJe hebt je %s succesvol geüpdatet.Misschien heb je het gemist, maar je hoeft geen gegevens te delen en kunt de opt-in %s.Je hebt reeds ingestemd met onze gebruiks-tracking, wat ons helpt om %s te blijven verbeteren.Je hebt reeds ingestemd met onze gebruiks-tracking, wat ons helpt om deze te blijven verbeteren.Uw %sAdd-on plan werd succesvol geüpgraded. Uw gratis %s proefperiode is succesvol opgezegd. Je account is succesvol geactiveerd met het %s plan.Je samenwerkingsaanvraag voor %s is geaccepteerd! Log in op je samenwerkingsomgeving op: %s.Je affiliate account is tijdelijk geschorst.Je e-mail werd succesvol geverifieerd - je bent GEWELDIG!Je gratis proefperiode is verlopen. %1$sUpgrade nu%2$som de %3$s zonder interrupties te blijven gebruiken. Je gratis proefperiode is verlopen. Je kan nog steeds al onze gratis features blijven gebruiken.Je licentie is geannuleerd. Als je denkt dat dat een fout is, neem dan alsjeblieft contact op met support.Je licentie is verlopen. %1$sUpgrade nu%2$s om de %3$s zonder interrupties te blijven gebruiken.Je licentie is verlopen. Je kan nog steeds alle %s features gebruiken, maar je zal je licentie moeten vernieuwen om weer updates en support te ontvangen.Je licentie is verlopen. Je kan echter de gratis %s voor altijd blijven gebruiken.Je licentie is succesvol geactiveerd.Je licentie is succesvol gedeactiveerd, je bent terug op het %s plan.Je naam is succesvol bijgewerkt.Je plan is succesvol veranderd naar %s.Je plan is succesvol geüpgraded.Je abonnement is succesvol geannuleerd. De licentie van je %s-plan al over %s aflopen.U proefperiode is met succes gestart.PostcodeToppieActiveer%s werkt niet zonder %s.%s werkt niet zonder de plug-in.Aankondigingtoestaan%s beschikbaarActiverenjaarAPIAfsluitenDebuggingGefeliciteerdGeblokkeerdVerbondenDownload NieuwsteDownload Nieuwste Gratis VersieMaandelijksVerloopdatumPadE-mail versturenmndJaarlijksJaarlijksEenmaligPlanGeen GeheimSDK VersiesLicentieSyncSync LicentieAuteurUitAangebaseerd op %sStart gratis proefperidoeAfsluitenAfsluitendeactiverendeligerenstuur mij %sGEEN%s beveiliging & feature updates, educatieve content of aanbiedingen.%s Plan%s gefactureerd BesteHoiOepsHoi %s,HoeralicentieSitesmsnieuwe versieniet geverifieerdPrijsPrijzenVersiesecstuur mij beveiliging & feature updates, educatieve content en aanbiedingen.overslaanHmmstart de proefperiodeabonnementoverschakelende meest recente %s versie hierproefperiodeProefperiodeVerwijderDowngradeBewerkVerbergOpt InOpt OutKoopToonSla OverBijwerkenUpgrade%s geledenfreemius/languages/freemius-ja.mo000064400000131742147600046700013111 0ustar00<\\S]%  6 @_#U y      H '!O:!!!!!! !!!!&!-""P" e"o"~""""5""" # #'#A# Z# g#q#o###$$"$$2%!:%a\%0%%&&'&/&C&K&T&\& a&o&&&& & &Y&,';' L'X'a'f'v'('1'%':(K(P(a(i( y( ((( (( ((( q) ~)))))))) *(* D*O*[*fC++ ++Y+a,~,,, , ,;,- --. . . ,.9.jH.. ..3. /~/U//0'0)B0-l00S01'1B1ME171g1p32222 2233+3 331=3.o3O33An4y4*5aJ55B5,5 6 %6 26?6]6 v666 6 666466 777 7(7 /7;7 B7 N7Z7t7 y7 777!77 7777%8++8W8 o8 }8/88m8,939;9 A9M9 V9 a9)o99949959(/:X:`:-w::U:;1;~ <[<<= = =%=(4=*]="=1=+=* >&4>N[>>>>.>)>)?9?Y?a? p?{???? ????W? B@P@Y@t@{@@@@ @@ @5@l@&lAAA AAAAAB B"B&'BLNBfBC CC%C +C 7CDC LCZC pC}CC$D/DD) E 5E @E\KEEEECE3FIFiFdF-TGG GG'GMGGH ZHdHjHpHuH{HEHHHHIII*'IRI*ZI^IIIIdI]J fJsJ JJJiJWK"vKBK6KL (L6L-GLuLL&LLLL$LMMjM/|MM>M N&"NZINTNRN.LO.{O9OZO3?P<sPbPPQUdQ_QRKR(SG*S#rS)S$S)ST!T>T;ST6T>TU U&UFU$\UUUUU&U*V7CV{VVV3VVW(W@WTW*qW1WWW#W"X>XPX `XlXXX XXNXYU7G Zdw U. 6C@ H<׉ 9!H[-^Ҋ1Zo--$ $3 Xelp f$*OH_WH hrǐ  #vBґ&   %2Jf |Buɒ$? dq!B$ 3' [h z;iÔ-ٕ  <I'e >B/<!+*!֙-]&'˚38 lv ;q̜t> ɝ ֝ ]_x!˞Ҟ9%K;   MZ1jˠҠr. y;Z"3 J;k"ʣA)*T[0bl; Sft*ۥ07eʦr0NB=5sK NUrY̪Tr3jT34'9\3ʮ ݮ?K.z   ˯د߯65 9FM`dkr yðʰ !&- 4AH ȱձ    $1 4> EO_uc ٲ 1BRYo v ų۳%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is the new owner of the account.%s minimum payout amount.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s tracking cookie after the first visit to maximize earnings potential.%s's paid features%sClick here%s to choose the sites where you'd like to activate the license on.APIAccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre you sure you want to delete all Freemius data?Are you sure you want to proceed?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBillingBlockingBlog IDBodyBusiness nameCan't find your license key?CancelCancel InstallationCancel SubscriptionCancel TrialCancelledCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanCheckoutCityClear API CacheClear Updates TransientsClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDelegate to Site AdminsDelete All AccountsDetailsDon't have a license key?Donate to this pluginDownloadDownload %s VersionDownload the latest %s versionDownload the latest versionDownloadedDue to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.During the update process we detected %d site(s) that are still pending license activation.During the update process we detected %s site(s) in the network that are still pending your attention.EmailEmail addressEndEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used for the upgrade below and we will resend you the license key.ErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.FreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFull nameFunctionGet commission for automated subscription renewals.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf you click it, this decision will be delegated to the sites administrators.If you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.In %sInstall Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid module ID.InvoiceIs ActiveIt looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's not what I was looking forJust letting you know that the add-ons information of %s is being pulled from an external server.KeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense KeyLicense keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerMessageMethodMobile appsModuleModule PathModule TypeMore information about %sNameNetwork BlogNetwork UserNewNew Version AvailableNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Opt InOpt OutOtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.PayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProcess IDProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicenseQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRequestsRequires WordPress VersionResultSDKSDK PathSave %sScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSimulate Network UpgradeSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSocial media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart TrialStart my free %sStateSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a new version of %s available.This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.TimestampTitleTotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser IDUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress.org Plugin PageYesYes - %sYou already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou do not have a valid license to access the premium version.You have a %s license.You have successfully updated your %s.You might have missed it, but you don't have to share any data and can just %s the opt-in.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onactive add-onActiveaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,interjection expressing joy or exuberanceYee-hawlike websitesSitesmillisecondsmsnot verifiednounPricenounPricingproduct versionVersionsecondssecsend me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialswitchingthe latest %s version heretrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Tomohyco Tsunoda, 2018 Language-Team: Japanese (http://app.transifex.com/freemius/wordpress-sdk/language/ja/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: ja Plural-Forms: nplurals=1; plural=0; X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 すぐに "%s" 有効化を完了してください%s のアドオンの支払いが完了しました。%sインストール%sラインセス%s 前%sとそのアドオンカスタマーが新規ライセンスを購入するごとに%sのコミッションが発生します。%s の無料試用が正常にキャンセルされました。 アドオンはプレミアムなので、自動的に無効化されました。 将来使用したい場合は、ライセンスを購入する必要があります。%s はプレミアムのみのアドオンです。そのプラグインを有効化する前にライセンスを購入する必要があります。%s は新しいオーナーです。%sお支払いの最低金額%sまたはそれ以上%s評価%s評価%s秒%sスター%sスター%s回%s回%s初回の訪問後、クッキーをトラッキングして収益の可能性を最大化しましょう。%sの有料機能%sここをクリックして%s ライセンスを有効化したいサイトを選択してください。APIアカウントアカウント詳細アクション有効化%sを有効化する%s プランを有効化フリーバージョンを有効化ライセンスを有効化保留中のサイトすべてでライセンスを有効にする。ネットワーク上にあるすべてのサイトのライセンスを有効にする。このアドオンを有効化有効化済み%s のアドオンモジュールのアドオン%sドメイン名を追加するアドオンアドオンアドオンが WordPress.org か Freemius にデプロイされている必要があります。住所住所欄 %dアフィリエイトアフィリエイト無料の %s の後は、わずか %s だけお支払ください。同意してライセンスを有効化すべてのリクエストすべてのタイプ許可して続けるまたは、今すぐスキップして、%sのネットワークレベルのアカウントページでライセンスを有効にすることもできます。総額%sから %s (有料版) の自動ダウンロードと自動インストールが%sで開始します。手動で行う場合は今すぐにキャンセルボタンをクリックしてください。匿名のフィードバック保留中のサイトすべてに反映させる。ネットワーク上にあるすべてのサイトに対して反映させる。アフィリエイトに応募するほんとうに全ての Freemius データを削除しますか?本当に続行していいですか?30日間の返金期間があるため、コミッションのお支払いは30日以降になります。自動インストールはオプトインしたユーザのみで動作します。%s に自動更新自動インストールレーティングの平均すごい!アフィリエイトになる請求書ブロッキングブログ ID本文商号ライセンスキーは見つかりませんか?キャンセルインストールをキャンセルするサブスクリプションをキャンセルするトライアルをキャンセルキャンセルトライアルをキャンセルするとすぐにすべてのプレミアム機能へのアクセスができなくなります。本当に実行しますか?ライセンスを変更オーナーを変更プラン変更チェックアウト市API キャッシュをクリアアップデートのトランジエントをクリアーにする匿名でプラグインを使用するにはこちらをクリッククリックして%sの評価をしているレビューを観るクリックしてフルサイズのスクリーンショットを見る %dプロダクトコード互換性のある最新バージョン連絡サポートに連絡連絡コントリビューター%s を有効化できません。国Cron タイプ日付無効化ライセンスを無効化ライセンスを無効化するとすべてのプレミアム機能が使えなくなりますが、他のサイトでライセンスを有効にすることができるようになります。本当に実行しますか?無効化デバッグログサイト管理者に委任する全てのアカウントを削除詳細ライセンスキーをお持ちではありませんか?このプラグインに寄付するダウンロード%s バージョンをダウンロード最新の %s をダウンロード最新版をダウンロードダウンロード済みアフィリエイト規約違反により、アフィリエイトアカウントを一時的に凍結させていただきました。ご質問等がありましたら、サポートにお問い合わせください。アップデートの処理中に%dサイトがライセンスの有効化が保留中であることを検知しました。アップデートの処理中に、ネットワーク内の%dサイトが対応待ちになっていることを検知しました。Emailメールアドレス終了%sのプロモーションを行う予定のあなたのサイトや他のサイトのドメイン名を入力してください。アップグレードに使用したメールアドレスを下に入力してください。そうすれば、ライセンスキーをお送りします。エラーサーバーからエラーを受信しました。期限切れ%s で期間終了追加のドメイン名プロダクトフォームのマーケティングを行う追加ドメイン名。ファイルフィルターWordPress.orgのガイドラインに準拠するため、トライアルを開始する前に、ユーザーと重要でないサイト情報のオプトイン、更新の確認やトライアルの状態確認のために%sが%sに対して定期的にデータを送信する許可を得るように設定してください。無料フリートライアルフリーバージョンFreemius APIFreemius デバッグFreemius SDK がプラグインのメインファイルを見つけることができませんでした。現在のエラーを添えて sdk@freemius.com に連絡してください。Freemius ステータスフルネーム機能サブスクリプションの自動更新でコミッションを得ましょう。ライセンスキーはお持ちですか?こんにちは。%sにアフィリエイトプログラムがあるのはご存知でしたか? %sがお好きなら、私たちのアンバサダーになって報酬を得ましょう!%s はどうですか? 私たちの全ての %s のプレミアム機能をお試しください。アップロードと有効化の方法どのように我々をプロモートしますか?もう払うことができませんどうしたら動作するか分かりませんでした。自分の情報を共有したくありませんより良い %sを見つけましたアカウントをアップグレードしましたが、ライセンスを同期しようとするとプランが %s のままです。%sはもう不要です短期間だけ %sが 必要です。ID決定をサイトの管理者に委任するにはクリックしてください。お時間があれば、なぜ%sするのか理由を教えてください。%sの所有権を%sへ譲りたい場合は、所有権の変更ボタンをクリックしてください。これらのサイトで%sを使う場合は、ライセンスキーを入力し、アクティベーションボタンをクリックしてください。%s 内フリーバージョンを今すぐインストールフリーバージョンの更新を今すぐインストール今すぐインストール今すぐ更新をインストールインストール中プラグイン: %sモジュール ID が不正です請求書有効ライセンスの有効化ができませんでした。ライセンスの無効化ができませんでした。すでにトライアルモードではないようなので、キャンセルする必要はありません :)まだ %s プランのようです。もしアップグレードやプランの変更をしたのなら、こちらで何らかの問題が発生しているようです。申し訳ありません。サイトは有効なライセンスを持っていないようです。認証パラメータの1つが間違っているようです。 公開鍵、秘密鍵、ユーザーIDを更新して、もう一度お試しください。探していたものではありません%s のアドオンに関する情報は、外部サーバーから取得されます。キー将来のユーザーのために修正できるよう、何が動作しなかったのかどうか共有してください…改善できるよう、どうか理由を教えてください。最終最終更新最新のライセンス最新のフリーバージョンがインストールされました最新版がイストールされました詳細はこちら長さライセンスライセンスキーライセンスキーライセンスキーが空です。ライフタイム%sは気に入りましたか? アンバサダーになって報酬を得ましょう ;-)DB オプションを読み込むlocalhostグロガーメッセージメソッドモバイルアプリケーションモジュールモジュールのパスモジュールタイプ%s に関する詳細情報名前ネットワークブログネットワークユーザ新規新しいバージョンがあります新しいフリーバージョン (%s) がインストールされました新しいバージョン (%s) がインストールされましたニュースレター次いいえID がありません%s の拘束はありません。いつでもキャンセルできます。%s 日以内であればいつでもキャンセルできます。クレジットカードは必要ありません。有効期限なし期限のない%sのプランにはトライアル期間はありません。O.K一度ライセンスの期限が切れると、フリーバージョンの利用は可能ですが、%sの機能を使うことができなくなります。オプトインオプトアウトその他所有者の Emailオーナー ID所有者名PCI コンプライアント有料アドオンは Freemius にデプロイされている必要があります。PayPal アカウントのメールアドレス支払いお支払いは USD かつ PayPal 経由で毎月行われます。プラン%s プランは存在しないため、試用を開始できません。%s プランにはトライアル期間はありません。プラン IDこちらで私たちに連絡をとってください。以下のメッセージとともに私たちに連絡をください。%s をダウンロードしてください。購入後すぐにメールで受け取ったライセンスキーを入力してください:関係のあるウェブサイトやソーシャルメディアの統計を提供してください。例: サイトの月間訪問者数、Emailの購読者数、フォロワー数等 (機密情報として取り扱います)アップグレードを完了するには以下の手順を完了させてください。セキュリティや機能のアップデート、学習用用コンテンツ、およびオファーについてお問い合わせを希望される場合は、お知らせください。どのように%sをプロモートするつもりなのか、詳細をお知らせください (できるだけ具体的にお願いします)フルネームを入力してください。プラグインプラグインのホームページプラグイン IDプラグインのインストール変更履歴説明FAQ機能 & 料金インストールその他の記述レビュープラグインはプレミアムコードバージョンのない「サービスウェア」です。プラグインプラグインとテーマを同期プレミアムプレミアムバージョンの %sは有効化に成功しました。プレミアムアドオンバージョンはすでにインストール済みです。プレミアムバージョンプレミアムバージョンはすでに有効になっています。料金表プライバシーポリシープロセス IDプロダクトプログラム概要プロモーション方法県・州・省公開鍵ライセンスを購入クォータ有効化メールを再送信新規カスタマーに私たちの%sを紹介して、売り上げごとに%sのコミッションを得ましょうライセンスを更新リクエスト数必要な WordPress のバージョン結果SDKSDK のパス%s を保存スケジュール Cronスクリーンショット住所で検索する秘密鍵外部ドメインで実行中のセキュアな HTTPS %sページトライアルのキャンセルに一時的な問題がありました。数分後に再度お試しください。最新版を取得できました。国を選択ライセンスキーを送信DB オプションを設定するネットワークアップグレードをシミュレートするシングルサイトライセンスサイト IDサイトのオプトインに成功しました。サイト数スキップと%sスラッグソーシャルメディア(Facebook、Twitter、その他)ご迷惑をおかけしてすいません。もし機会をいただけたらお手伝いをします。メールアドレスのアップデートを完了できませんでした。他のユーザーがすでに同じメールアドレスで登録しているようです。開始トライアルを開始無料の %s を開始州送信と%sサブスクリプションサポートサポートフォーラムサーバーからのデータを同期税金 / VAT ID利用規約アフィリエイトアカウントに応募いただきありがとうございます。残念ながら現時点では申請を受理することができませんでした。30日後に改めてお申込みください。アフィリエイトプログラムに応募いただきありがとうございます。14日以内にお申し込み詳細をレビューし、改めてご連絡いたします。%sとアドオンのご利用ありがとうございます!%sのご利用ありがとうございます!プロダクトのご利用ありがとうございます!ありがとうございます!ありがとう $s さん!所有権の変更を確認していただきありがとうございます。 %s に承認メールが送信されました。%s の影響でサイトを崩れました%s が動作しませんでした%sが期待通りに動きませんでした %s は素晴らしいのですが、サポートされていないある機能が必要です%s が動作していません%s の動作が突然停止しましたインストールプロセスが開始され、数分で完了します。完了までしばらくお待ちください。ページのリフレッシュなどは行わないでください。リモートプラグインパッケージには、目的のスラッグを含むフォルダが含まれていないため、リねームが機能しませんでした。%s のアップグレードが完了しました。テーマテーマ変更テーマ%sの入手可能な新しいバージョンがありますこのプラグインはインストールされた WordPress のバージョンに互換性がありません。このプラグインはインストールされた WordPress のバージョンでは検証されていません。タイムスタンプタイトルトータル町トライアルタイプファイルシステムに接続できません。視覚情報を確認してください。無制限ライセンス無制限のアップデート無制限のコミッション。%sサイトまで更新ライセンスを更新更新、発表、マーケティング、スパムなしアップグレードダウンロードしたバージョンをアップロードして有効化やったーユーザー IDユーザー値%s に確認メールを送信しました。もし5分以内にそれが届かない場合、迷惑メールボックスを確認してください。認証済み認証メールバージョン %s をリリースしました。詳細を表示有料の機能を表示する警告メールアドレスに関連付けられた有効なライセンスが見つかりません。メールアドレスが正しいか確認してください。システムではメールアドレスを見つけることができませんでした。メールアドレスが正しいか確認してください。プラグインを微調整します、 %s, %sFreeminus ネットワークレベルのインテグレーションをご紹介できることに興奮しています。ウェブサイト、Email またはソーシャルメディアの統計 (オプション)何を期待していましたか?何の機能ですか?自分の %s はなんですか? 支払ってもよいと思う価格はいくらですか?探していたのは何ですか?%sの名前は何ですか?%sのプロモーションを行うサイトはどこですか?WordPress.org のプラグインページはいはい以前すでに試用版を利用しました。%2$s プランの%1$s日間のフリートライアルを開始するまであとワンクリックです。すべて完璧です!すでに%sをトライアルモードで利用中です。もうあとわずかです - %sプレミアムバージョンにアクセスできる有効なライセンス持っていません。%s ライセンスを持っています。%s のアップデートが成功しました。見逃していたかもしれませんが、どんな情報も共有する必要はなく、オプトインを $s することができます。 %sの改善に役立つ使用状況のトラッキングにすでにオプトインしています。プロダクトの改善に役立つ使用状況のトラッキングにすでにオプトインしています。%s のアドオンのプランのアップグレードが完了しました。%s のフリートライアルはキャンセルされました。アカウントが %s プランで有効化できました。%sのアフィリエイト申請は受理されました! 次のリンクからアフィリエイトエリアにログインしてください:%sアフィリエイトアカウントは一時的に停止されました。あなたのメールアドレスの承認が完了しました。すごい!フリートライアル期間が終了しました。%1$s %3$sに邪魔されずに利用を継続するには,今すぐ %2$s のアップグレードを行ってください。フリートライアル期間が終了しました。無料で使える機能は引き続き利用可能です。ライセンスはキャンセルされました。もしそれが間違いだと思うならサポートに連絡してください。ライセンスの有効期限が切れました。 %1$s %3$sに邪魔されずに利用を継続するには,今すぐ%2$sアップグレードを行ってください。ライセンスは有効期限がきれました。%s の機能を引き続き利用することができます。ただし、アップデートやサポートをうけるにはライセンスをアップデートする必要があります。ライセンスの有効期限が切れました。無料バージョンの%s は引き続き利用できます。ライセンスの有効化が成功しました。ライセンスの無効化が完了しました。%s プランに戻りました。名前のアップデートが成功しました。プランの %s への変更が成功しました。プランのアップグレードが成功しました。トライアル版の利用を開始しました。ZIP / 郵便番号そうだ有効%s は、%s が無いと実行することができません。%s は、プラグインが無いと実行することができません。警告許可あと %s有効化中年API却下デバッグおめでとうブロック接続最新版をダウンロード最新のフリーバージョンをダウンロード月期限切れパスメール送信中月年次毎年一度プラン秘密鍵がありませんSDK バージョンライセンス同期ライセンスを同期作者オフオン%sを基にフリートライアルを開始却下却下無効化中代表セキュリティと機能のアップデート、学習用コンテンツやオファーを%s送らないでください%s。%s プラン%s への請求ベストヘイおっとおおい %s さん、ヤッホーサイト数ms未認証料金料金表バージョン秒セキュリティと機能のアップデート、学習用コンテンツやオファーを送ってください。スキップふむトライアルを開始変更中最新の %s バージョンはこちらです。トライアル削除ダウングレード編集非表示オプトインオプトアウト購入表示スキップ更新アップグレード%s 前freemius/languages/freemius-it_IT.mo000064400000162124147600046700013525 0ustar00sL'L'AM''n2(2(Z(h/)S)%) * ***1*D*6*!+_+6,V,#m,,%, , , ,,,- --@-+]-H--O-j5..>[?f^?? ???@ @Y @az@@@@A &A 4A;BA~AAAB B B BBjB-C5FMF7Fg,GpGHHy%HHH HHH I.I%AI gIIIII II1J.JOJ3KAKKyL2LLLaLZMB^M,MM M MM N $N/N6N>N PN [NgN wNNN4NN NNNNOOO&O6O QO]O dO pO|OO O OOO O!OO P%P*P%-PSP%YP+PP P P/PQmQ~QQRR*R 0Rpp p&q<FqDqZqT#rRxr.r.r)s-;t9itZt3t<2ubouPuU#v_yvvKtw(wGw#1x%Ux){x$xUx) yJy\yyyy;y6y>zXz^zyzz$zzz {&{&D{*k{7{{{|3|P|e|{|||*|1|!}=}#Q}u}}} }}}} ~~ ~"~N+~z~~~~~~ 1*\dx      9CHQ  $?E Xd s }  ΁ ہKKsgyX":.] :ΊU)%E1kʌ ь ی^0V[B|FÎgkt} ȏ,.;j Ő̐<Ґ  + 5+Bn t3;MޒY,"^"o#"+ٔ7=g\)ĕSB[t  ˖Ԗܖ  $F6#} eq Ә ƙ'ؙ - I9@Ě͚Ԛ  #A IV [e}w >]yA#ĝ|e!ɞ :Dvw ¡JO"Т *7<ty$ Ϥܤp9 ʥ?ӥ;O{jh#Os!)٧^|+¨ŨIL)]l#^!#6 Zg(ƫ-ܫ )1Of 0+<\f=&ծ~A{ ޯJB[I/հܰ# $ E R\d v ٱA) ?IMT nxȲϲ߲ ,2 M'X% * ,C!"e4޴ue ۵##6F X=f Ŷ7 ;IEX-%%4 Z[fC¸5 nS* )3 L Vbf  U7'-_ýڽ  ,6FWk'[  4>!U"w  ¿ʿ ݿ@ |Ln-8fxI-C \h  9(Q ] #!2 8D J Uaj|j7'Jr( z,Cpx&H* K_ u7 *U!Iw8   @+D\kt* %  #"{)gs %M88.g~$"6#Mqt#|*K>2Vj',KE*&==dG}FhC<D0u3w?i@UHhgH\e sW;/J.,=-j*]4!VZ^u |'    %."H kw      #+OV]bi_   *08=@Tcry FRIO#Sw   !*17 @N %s to access version %s security & feature updates, and support. The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box. The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s"The ", e.g.: "The plugin"The %s's%1$s has been placed into safe mode because we noticed that %2$s is an exact copy of %3$s.%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s automatic security & feature updates and paid functionality will keep working without interruptions until %s (or when your license expires, whatever comes first).%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is my client's email address%s is my email address%s is the new owner of the account.%s minimum payout amount.%s opt-in was successfully completed.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s to access version %s security & feature updates, and support.%s to activate the license once you get it.%s tracking cookie after the first visit to maximize earnings potential.%s's paid features%sClick here%s to choose the sites where you'd like to activate the license on.A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours.A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder.APIASCII arrow left icon←ASCII arrow right icon➤AccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.An unknown error has occurred while trying to set the user's beta mode.An unknown error has occurred while trying to toggle the license's white-label mode.An unknown error has occurred.An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre both %s and %s your email addresses?Are you sure you want to delete all Freemius data?Are you sure you want to proceed?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Associate with the license owner's account.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBetaBillingBilling & InvoicesBlockingBlog IDBodyBundleBundle PlanBusiness nameBuy a license nowBuy licenseBy changing the user, you agree to transfer the account ownership to:Can't find your license key?CancelCancel %s & ProceedCancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site.Cancel %s?Cancel InstallationCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanChange UserCheckoutClear API CacheClear Updates TransientsClick hereClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site.Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDebug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the "Stop Debug" link.Delegate to Site AdminsDelete All AccountsDetailsDiagnostic data will no longer be sent from %s to %s.Disabling white-label modeDon't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support.Don't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload Paid VersionDownload the latest %s versionDownload the latest versionDownloadedDue to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.Duplicate WebsiteDuring the update process we detected %d site(s) that are still pending license activation.During the update process we detected %s site(s) in the network that are still pending your attention.EmailEmail addressEmail address updateEnabling white-label modeEndEnter email addressEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used for the upgrade below and we will resend you the license key.Enter the new email addressErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.FreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFreemius is our licensing and software updates engineFull nameFunctionGet commission for automated subscription renewals.Get updates for bleeding edge Beta versions of %s.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I Agree - Change UserI can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf this is a long term duplicate, to keep automatic updates and paid functionality after %s, please %s.If you click it, this decision will be delegated to the sites administrators.If you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.Important Upgrade Notice:In %sIn case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?Install Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid clone resolution action.Invalid module ID.Invalid new user ID or email address.Invalid site details collection.InvoiceIs %2$s a duplicate of %4$s?Is %2$s a new website?Is %2$s the new home of %4$s?Is ActiveIs this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin.It looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It requires license activation.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's a temporary %s - I'm troubleshooting an issueIt's not what I was looking forJoin the Beta programJust letting you know that the add-ons information of %s is being pulled from an external server.KeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense IDLicense KeyLicense issues?License keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerLong-Term DuplicateMessageMethodMigrateMigrate LicenseMigrate Options to NetworkMobile appsModuleModule PathModule TypeMore information about %sNameNetwork BlogNetwork UserNewNew Version AvailableNew WebsiteNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo - only move this site's data to %sNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license.Opt InOpt OutOpt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.Part of an activation link message.Click herePart of the message telling the user what they should receive via email.a license keyPart of the message telling the user what they should receive via email.the installation instructionsPart of the message that tells the user to check their spam folder for a specific email.the product's support email addressPayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please enter the license key to enable the debug mode:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.Please provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProceedProcess IDProcessingProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicensePurchase MoreQuick FeedbackQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRenew your license nowRequestsRequires PHP VersionRequires WordPress VersionReset Deactivation SnoozingResultSDKSDK PathSave %sSavedScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes.Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSharing diagnostic data with %s helps to provide functionality that's more relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the plugin should be translated and tailored to.Show error detailsSimulate Network UpgradeSimulate Trial PromotionSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSnooze & %sSo you can reuse the license when the %s is no longer active.Social media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart DebugStart TrialStart my free %sStateStop DebugSubmitSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you for updating to %1$s v%2$s!Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.Thanks for upgrading.Thanks!The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe following products'The installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The products below have been placed into safe mode because we noticed that %2$s is an exact copy of %3$s:%1$sThe products below have been placed into safe mode because we noticed that %2$s is an exact copy of these sites:%3$s%1$sThe remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a %s of %s available.There is a new version of %s available.This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.This plugin requires a newer version of PHP.TimestampTitleTo enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your "My Profile" section of your User Dashboard:TotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser DashboardUser IDUser keyUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We couldn't load the add-ons list. It's probably an issue on our side, please try to come back in few minutes.We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)Welcome to %s! To get started, please enter your license key:What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress.org Plugin PageWould you like to merge %s into %s?Would you like to proceed with the update?YesYes - %sYes - both addresses are mineYes - move all my data and assets from %s to %sYes, %2$s is a duplicate of %4$s for the purpose of testing, staging, or development.Yes, %2$s is a new and different website that is separate from %4$s.You already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.You do not have a valid license to access the premium version.You have a %s license.You have purchased a %s license.You have successfully updated your %s.You marked this website, %s, as a temporary duplicate of %s.You marked this website, %s, as a temporary duplicate of these sitesYou might have missed it, but you don't have to share any data and can just %s the opt-in.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s.Your %s license was successfully deactivated.Your account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully activated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your subscription was successfully cancelled. Your %s plan license will expire in %s.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onactivate a license hereactive add-onActiveaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdatadaysdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,hourhoursinstalled add-onInstalledinterjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew Beta versionnew versionnot verifiednounPricenounPricingoptionalproduct versionVersionproductsrevert it nowsecondssecseems like the key you entered doesn't match our records.send me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe above-mentioned sitesthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Daniele Scasciafratte Mte90 , 2015-2019,2022,2024 Language-Team: Italian (Italy) (http://app.transifex.com/freemius/wordpress-sdk/language/it_IT/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: it_IT Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2; X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 %sper accedere alla versione%s per aggiornamenti, funzionalità e supporto.Il %s %slink per scaricare%s, la licenza e le istruzioni d'installazione sono state inviate a %s. Se non trovi l'email entro 5 minuti cerca nella cartella dello spam.La versione a pagamento di %1$sè già installata. Attiva questione versione per iniziare ad usare le funzionalità di %2$s.%3$sIl %s%1$s è entrato in modalità sicurezza perché è stato rilevato che %2$s è una copia esatta di %3$s./%1$sfermerà tutti i pagamenti ricorrenti futuri e il tuo piano %2$sche scadrà in %3$s.Completa l'attivazione di "%s" oraL' add-on %s è stato acquistato con successo.%s Installazioni%s Licenze%s fa%se i suoi addon%s con aggiornamenti automatici di sicurezza e funzionalità con le funzionalità a pagamento manterrà il funzionamento senza interruzioni fino a %s (o quando la licenza scade, oppure quello che avviene prima).%scommissione quando un utente acquista una nuova lcienza.Il periodo di prova gratuito %s è stato annullato con successo. Siccome l'add-on è premium, è stato disattivato automaticamente. Se vorrai usarlo in futuro, dovrai comprare una licenza.%s è un add-on premium. Devi comprare una licenza prima di poter attivare il plugin.%s è il mio indirizzo email%s è il mio indirizzo email%s è il nuovo proprietario dell'account.%s quantità minima per il pagamento.%s l'iscrizione è stata completata con successo.%s o superiore%s valutazione%s valutazioni%s sec%s stella%s stelle%s volta%s volte%sper accedere alla versione %sper aggiornamenti di sicurezza, nuove funzionalità e supporto.%s per attivare la licenza dopo averla ricevuta.%s cookie di tracciamento dopo che la prima visita per massimizzare i margini di guadagno. Funzionalità a pagamento di %s%sClicca qui%s per scegliere i siti dove vuoi attivare la licenza.Un email di conferma è stata inviata a %s. Il proprietario dell'email deve confermare l'aggiornamento nelle prossime 4 ore.Una email di conferma è stata inviata a %s. Puoi confermare l'aggiornamento nelle prossime 4 ore. Se non puoi trovare l'email verifica la tua cartella dello spam.API←➤AccountDettagli dell'accountAzioniAttivaAttiva %sAttivare il piano %sAttiva le funzionalità di %sAttiva versione gratuitaAttiva licenzaAttiva le licenze su tutti i siti in attesa.Attiva la licenza su tutti i siti del network.Attivare questo addonAttivatoAdd-on per %sAddon del modulo %sAggiungi un altro dominioAdd-onAddonL'add-on dev'essere distribuito da WordPress.org o Freemius.IndirizzoRiga indirizzo %dAffiliatiAffiliazioneDopo il tuo %s gratuito, paghi solamente %sAccetta e attiva la licenzaTutte le richiesteTutti i tipiConsenti & ContinuaIn caso puoi saltare per adesso e attivare la licenza successivamente nella tua pagina di attivazione network di %s.ImportoUn download con installazione automatica di %s (versione a pagamento) da %s inizierà in %s. Se preferisci farlo manualmente, fai clic sul pulsante per annullare.Un errore sconosciuto è avvenuto durante l'attivazione della modalità beta.Un errore sconosciuto è avvenuto mentre si passava alla licenza in modalità whitelabel.Un errore sconosciuto è avvenuto.Un aggiornamento per la versione Beta sostituirà la versione installata di %scon l'ultima versione Beta, utilizzare con attenzione e non su siti in produzione. Sei stato avvisato!Feedback anonimoApplica su tutti i siti in attesa.Applica su tutti i siti della rete.Applica per diventare un affiliatoSono entrambi %s e %s tuoi indirizzi email?Sei sicuro di voler eliminare tutti i dati di Freemius?Sei sicuro di voler procedere?Ci riserviamo 30 giorni in caso di rimborsi, paghiamo le commissioni se sono più vecchie di 30 giorni.Associa con il proprietario della licenzaL'installazione automatica funziona solo per gli utenti che hanno dato il consenso.Rinnovo automatico in %sInstallazione automaticaValutazione mediaFantasticoDiventa un affiliatoBetaFatturazioneRicevute e FattureBloccatoBlog IDBodyPacchettoPiano BundleNome della compagniaCompra una licenza oraCompra la licenzaCambiando l'utente accetti di trasferire la proprietà dell'account a:Non trovi la tua chiave di licenza?AnnullaAnnulla %s & ProseguiAnnulla %s, non ho bisogno di aggiornamenti di funzionalità e sicurezza o supporto per %sperché non ho intenzione di usare %ssu questo sito o qualunque altro sito.Annulla %s?Annulla installazioneAnnulla sottoscrizioneAnnulla prova gratuitaAnnullatoCancellazione di %sCancellazione %s...Cancella la sottoscrizioneCancellando il periodo di prova gratuito bloccherai immediatamente l'accesso a tutte le funzionalità premium. Vuoi continuare?Cambia licenzaCambia ProprietarioCambia pianoCambia utenteCassaElimina cache APISvuota le Transient degli aggiornamentiClicca quiFai clic qui per usare il plugin anonimamenteFai clic per vedere le recensioni che hanno fornito una valutazione di %sFare clic per visualizzare lo screenshot in grandi dimensioni %dProdottiCodiceCompatibile fino aContattiContatta il supportoContattaciContributoriNon é stato possibile attivare %s.NazioneTipo di CronDataDisattivaDisattiva licenzaDisattivare o disinstallare %s che disabiliterà automaticamente la licenza, che permetterà di utilizzarla in un altro sito.Disattiva la tua licenza bloccando tutte le funzionalità premium ma potrai attivare la licenza su un altro sito. Sei sicuro di voler continuare?DisattivazioneDebug LogLa modalità Debug è stata attivata con successo e sarà disattivata automaticamente in 60 minuti. Puoi disattivarla prima cliccando sul link "Ferma Debug".Delega ai proprietari del sitoEliminare tutti gli accountDettagliLe informazioni diagnostiche non saranno più inviate da %s a %s.Disabilita la modalità white labelNon annullare %s, sono interessato in ottenere gli aggiornamenti di sicurezza, nuove funzionalità o contattare il supporto.Non hai una chiave di licenza?Fai una donazione a questo pluginTorna al piano precedenteDownloadScarica la versione %sScarica la versione a pagamentoScarica l'ultima versione di %sScarica l'ultima versioneScaricatoCausa la %sDirettiva per la protezione dei Dati Europea (GDPR)%sabbiamo adeguato i requisiti che fornisci per il consenso, confermando che accetti di lasciare i dati.A causa della violazione dei nostri termini di affiliazione abbiamo deciso di bloccare temporaneamente il tuo account affiliativo. Se hai domande contatta il supporto.Sito duplicatoDurante la procedura di aggiornamento abbiamo individuato%d sito/i che sono in attesa della attivazione della licenza.Durante la procedura di aggiornamenti abbiamo individuato %s sito/i del network che sono in attesa di un tuo controllo.EmailIndirizzo emailAggiorna l'indirizzo emailAbilita la modalità white labelFineInserisci l'indirizzo emailInserisci il dominio del tuo sito o altri siti da dove vuoi promuovere %s.Inserisci qui sotto l'indirizzo email che hai usato per registrare l'aggiornamento e ti invieremo di nuovo la chiave di licenza.Inserisci il nuovo indirizzo emailErroreErrore ricevuto dal server:ScadutoScade in %sDomini aggiuntiviDomini aggiuntivi dove ci sarà il modulo promozionale.FileFiltroPer essere accettato del regolamento WordPress.org, prima di attivare il periodo di prova devi accettare di condividere informazioni come il tuo utente e dati non sensibili. Permettendo a %s di inviare dati periodicamente a %s per verificare gli aggiornamenti e approvare il periodo di prova.GratuitoProva gratuitaVersione gratuitaFreemius APIDebug FreemiusL'SDK di Freemius non è riuscito a trovare il file principale del plugin. Per favore contatta sdk@freemius.com riportando l'errore.Stato di FreemiusFreemius è il nostro servizio di licenze e aggiornamentiNome completoFunzioneOttieni delle commissioni dal sistema automatizzato di rinnovo.Ottieni gli aggiornamenti per le nuove versioni Beta di %s.Hai una chiave di licenza?Ciao, sai che %s ha il programma di affiliazione? Se ti piace %s puoi diventare un nostro ambasciatore e guadagnare denaro!Come sta andando con %s? Prova tutte le funzionalità premium di %s con una prova gratuita di %d giorni.Come faccio a caricare ed attivare?Come ci promuoverai?Accetto - Cambia utenteNon posso piú pagarloNon capisco come farlo funzionareNon voglio condividere i miei dati con teHo trovato un migliore %sHo aggiornato il mio account, ma quando cerco di sincronizzare la licenza, il piano rimane %s.Non ho più bisogno di %sHo avuto bisogno di %s per un breve periodoIDSe questo è un duplicato a lungo termine per mantenere gli aggiornamenti automatico e funzionalità a pagamento dopo %s, verifica %s.Se fai clic questa decisione sarà delegata agli amministratori del sito.Se hai un attimo, facci sapere perché %sPuoi abbandonare la proprietà dell'account %s a %scliccando il pulsante Cambia proprietario.Se vuoi utilizzare %s su questi siti, inserisci la tua licenza sotto e fai clic sul pulsante di attivazione.Avviso Importante di aggiornamento:In %sIn caso NON hai pianificato di usare %s su questo sito (o ogni altro sito) vuoi cancellare %s?Installa la versione gratuita oraInstalla l'ultima versione gratuitaInstalla oraInstalla l'aggiornamento oraInstallazione plugin: %sAzione di risoluzione del clone fallita.ID modulo non valida.Nuovo utente ID o indirizzo email non valido.Raccolta dati siti non valida.Fattura%2$s è un duplicato di %4$s?%2$s è un nuovo sito?%2$s è la nuova home di %4$s?è attivaSi tratta di un sito di un cliente?%sse vuoi puoi nascondere informazioni sensibili come la tua email, licenza, prezzi e indirizzi dalla tua bacheca di WP.Sembra che la licenza non possa essere attivata.Sembra che la disattivazione della licenza non sia riuscita.Sembra che tu non stia più usando la prova gratuita, quindi non c'è niente che tu debba annullare :)Sembra che tu sia ancora usando il piano %s. Se hai effettuato un upgrade o cambiato il piano, è probabile che ci sia un problema nei nostri sistemi.Sembra che il tuo sito non disponga di alcuna licenza attiva.Richiede la attivazione della licenza.Sembra che uno dei parametri di autenticazione sia sbagliato. Aggiorna la tua chiave pubblica, Secret Key & User ID e riprova.È una %s temporanea. Sto solo cercando di risolvere un problema.Non é quello che stavo cercandoEntra nel programma BetaLe informazioni sugli add-on di %s vengono scaricate da un server esterno.ChiaveCondividi cosa non ha funzionato in modo da migliorare il prodotto per gli utenti futuri...Spiegandoci il motivo ci aiuterai a migliorare.UltimoUltimo aggiornamentoUltima licenzaUltima versione gratuita installataVersione più recente installataScopri altroLunghezzaLicenzaLicense AgreementLicense IDChiave della licenzaProblemi di licenza?Chiave di licenzaLa chiave licenza è vuota.Tutta la vitaTi piace %s? Diventa il nostro ambasciatore e guadagna denaro ;-)Carica opzioni del DBLocalhostLogLoggerDuplicato a lungo termineMessaggioMetodoSpostaSposta la licenzaMigra le opzioni al NetworkApplicazioni mobileModuloPercorso moduloTipo di moduloUlteriori informazioni su %sNomeNetwork BlogUtente NetworkNuovoNuova versione disponibileNuovo sitoNuova versione gratuita (%s) installataVersione più recente (%s) installataNewsletterSuccessivoNoNo sposta solo i dati di questo sito su %sNessun IDNessun impegno con %s - cancella quando vuoiNessun impegno per %s giorni - puoi annullare in qualsiasi momento!Nessuna carta di credito richiestaNessuna scadenzaNon in scadenzaNessuno dei piani di %ssupporta il periodo di prova.OKQuando la tua licenza scadrà, potrai comunque continuare a usare la versione gratuita, ma NON avrai accesso alle funzionalità %s.Quando la tua licenza scadrà non potrai più usare %s, a meno che lo attivi di nuovo con una licenza premium valida.IscrivitiCancella iscrizioneAbilita "%s" per renderlo migliore!AltroEmail proprietarioID proprietarioNome proprietarioPCI compliantGli add-on a pagamento devono essere distribuiti da Freemius.Clicca quiuna chiave di licenzale istruzioni di installazionel'indirizzo email per il supporto relativo al prodotto.Indirizzo account email PaypalPagamentiI pagamenti sono in Dollari Americani e processati mensilmente da PayPal.PianoIl piano %s non esiste, per questo motivo non è possibile iniziare il periodo di prova.Il piano %s non supporta il periodo di prova.ID PianoContattaci quiContattaci con il seguente messaggio:Scarica %s.Per favore inserisci la chiave di licenza che hai ricevuto via mail subito dopo l'acquisto:Inserisci la chiave della licenza per abilitare la modalità Debug:Facci sapere ogni sito o statistiche social valide, es: visite uniche mensili, numero di sottoscrizioni email, follower ecc (tratteremo queste informazioni come riservate).Segui i passi seguenti per completare l'aggiornamentoFacci sapere se vuoi essere contattato per aggiornamenti di sicurezza e di funzionalità, contenuti formativi e offerte occasionali:Si prega di notare che non saremo in grado di garantire lo stesso prezzo per rinnovi/sottoscrizioni dopo la cancellazione. Se scegli di rinnovare l'abbonamento manualmente in futuro, dopo un aumento del prezzo, che di solito avviene una volta l'anno, ti verrà addebitato il nuovo prezzo.Fornisci i dettagli su come intendi promuovere %s. (sii più esplicativo possibile)Per favore inserisci il tuo nome completo.PluginHomepage del pluginPlugin IDInstallazione del pluginChangelogDescrizioneFAQCaratteristiche & prezziInstallazioneAltre noteRecensioniIl plugin è un "Serviceware", quindi non dispone di una versione del codice Premium.PluginSincronizzazione plugin e temiPremiumLa versione 1%s Permium è stata attivata con successo.Versione Premium dell'add-on già installata.Versione premiumVersione Premium già attiva.PrezziPolitica sulla privacyProseguiID processoElaborazioneProdottiSommario programmaMetodi promozionaliProvinciaChiave pubblicaAcquista licenzaContinua a comprareSuggerimenti rapidiQuotaInvia nuovamente l'email di attivazioneComunica nuovi clienti al nostro %s e guadagna %s di commissione per ogni vendita avvenuta!Rinnova licenzaRinnova la tua licenza oraRichiesteVersione PHP richiestaRichiede la versione di WordPressResetta l'avviso di disattivazioneRisultatoSDKPercorso SDKRisparmia %sSalvatoAzioni programmateScreenshotCerca per indirizzoChiave segretaPagina sicura su protocollo HTTPS %s eseguita su dominio esternoSembra che stai avendo dei problemi temporanei con la cancellazione della sottoscrizione. Prova nuovamente tra pochi minuti.Stiamo avendo qualche problema temporaneo con l'annullamento del periodo di prova. Riprova tra qualche minuto.Sembra che tu abbia la versione più recente.Seleziona NazioneInvia chiave di licenzaImposta opzione del DBCondividere informazioni diagnostiche con %s aiuta a fornire funzionalità mirate al vostro sito web. La condivisione dei dati permette, inoltre, di evitare errori di incompatibilità relative a versioni di WordPress o PHP, e di determinare in quali lingue, e per quali regioni, il plugin necessita di traduzioni e miglioramenti.Mostra i dettagli dell'errore.Simula aggiornamento networkSimula la prova TrialLicenza per sito singoloID del sitoSito accettato con successo.SitiSalta & %sSlugSilenzia e %sPuoi riutilizzare la licenza quando %s non è piu attivo.Social network (Facebook, Twitter, ecc.)Siamo spiacenti per l'inconveniente e siamo qui per aiutarti con il tuo permesso.Siamo spiacenti, non siamo riusciti a completare l'aggiornamento via email. Un altro utente con lo stesso indirizzo email è già registrato.AvviaAvvia DebugInizia il periodo di prova gratuitoInizia la mia %sStatoFerma DebugInviaInvia e %sSottoscriviSupportoForum di supportoSincronizza i dati dal serverNumero Partita Iva o VATTermini del ServizioGrazie per la partecipazione al nostro programma di affiliazione, sfortunatamente abbiamo valutato di rifiutare la tua richiesta. Prova nuovamente fra 30 giorni.Grazie per la partecipazione al nostro programma di affiliazione, valuteremo la tua richiesta durante i prossimi 14 giorni e ti contatteremo per maggiori informazioni.Grazie per avere installato l'aggiornamento %1$s v%2$s.Grazie per utilizzare %se i suoi addon!Grazie per utilizzare %s!Grazie per utilizzare i nostri prodotti!Grazie!Grazie %s!Grazie per aver confermato il cambiamento del proprietario. Un' email è stata appena inviata a %s per la conferma finale.Grazie per avere installato l'aggiornamento.Grazie!%s ha rotto il mio sito%s non funziona%s non ha funzionato come mi aspettavo%s è ottimo ma ho bisogno di una funzionalità specifica non supportata%s non funziona%s ha improvvisamente smesso di funzionareI prodotti seguentiIl processo d'installazione è iniziato e potrebbe impiegare alcuni minuti per completarsi. Attendi finchè non ha finito, assicurandoti di non ricaricare questa pagina.Il prodotto sottostante è stato messo in modalità sicurezza perchè abbiamo notato che %2$s è una copia esatta di %3$s: %1$sIl prodotto sottostante è stato messo in modalità sicurezza perchè abbiamo notato che %2$s è una copia esatta di questi siti %3$s: %1$sIl pacchetto remoto del plugin non contiene una cartella con lo slug desiderato e la rinominazione non ha funzionato.L'aggiornamento di %s è stato completato con successo.TemaCambio temaTemiC'è un %sdi %s disponibile.C'è una nuova versione di %s disponibile.Questo plugin non è stato segnato come compatibile con la tua versione di WordPress.Questo plugin non è stato testato con la versione corrente di WordPress.Questo plugin richiede una versione di PHP più recente.TimestampTitoloAbilita la modalità Debug, inserisci la chiave segreta del proprietario della licenza (UserID = %d), che puoi trovare nella sezione "Profilo" della dashboard utente:TotaleCittadinaProva gratuitaTipoImpossibile accedere al filesystem. Conferma le tue credenziali.Licenze illimitateAggiornamenti IllimitatiCommissioni illimitate.Fino a %s sitiAggiornaAggiorna licenzaAggiornamenti, annunci, marketing, no spamAggiornamentoCarica e attiva la versione scaricataForteBacheca UtenteID utenteChiave utenteUtentiValoreL'email di verifica è stata inviata a %s. Se dopo 5 minuti non è ancora arrivata, per favore controlla nella tua casella di posta indesiderata.VerificatoVerifica emailLa versione %s é stata rilasciata.Visualizza dettagliVedi funzionalità a pagamentoAvvisoNon siamo riusciti a trovare alcuna licenza attiva associata al tuo indirizzo email, sei sicuro che sia l'indirizzo giusto?Non siamo riusciti a trovare il tuo indirizzo email nel sistema, sei sicuro che sia l'indirizzo giusto?Non possiamo caricare la lista degli addon. Probabilmente è un nostro problema, prova di nuovo fra qualche minuto.Abbiamo fatto alcune migliore a %s,%sSiamo felici di presentarvi il supporto al sistema multi network di Freemius.Siti, email e statistiche dei social network (opzionali)Benvenuto nel %s! Per iniziare inserisci la tua licenza:Che cosa ti aspettavi?Quale funzionalitá?Qual è il tuo %s?Che prezzo ritieni opportuno pagare?Che cosa stai cercando?Qual è il nome di %s?Dove vuoi promuovere %s?Pagina dei plugin di WordPress.orgVuoi fondere %s in %s?Vuoi procedere con l'aggiornamento?SiSI - %sSi entrambi gli indirizzi sono mieiSi muovi tutti i miei e risorse da %s a %sSi, %2$s è un duplicato di %4$s per il motivo di test, staging o sviluppo.Si %2$s è un nuovo e sito differente che è separato da %4$s.Hai già utilizzato una prova gratuita in passato.Sei a un clic di distanza dall'iniziare il tuo periodo di prova gratuito di %1$s giorni per il piano %2$s.Sei fantastico!Stai già usando %s in modalità prova.Sei a un passo dalla fine - %sPuoi continuare ad utilizzare le funzionalità%sma non avrai accesso agli aggiornamenti di sicurezza, nuove funzionalità o supporto.Non disponi di una licenza valida per accedere alla versione Premium.Hai la licenza %s.Hai la licenza %s.Hai aggiornato con successo il tuo %s.Hai segnato questo sito, %s, come duplicato temporaneo di %s.Hai marchiato questo sito, %s, come duplicato temporaneo di questi sitiPotresti non averci fatto caso, ma non sei obbligato a condividere i tuoi dati e puoi semplicemente %s la tua partecipazione.Hai già accettato il tracciamento d'uso, ci aiuterà a migliorare %s.Hai già accettato il tracciamento d'uso che ci aiuta a migliorare.Il piano del tuo add-on %s è stato aggiornato con successo.Il tuo periodo di prova gratuito %s è stato annullato con successo.La tua licenza%s è stata segnata come white label per nascondere informazioni sensibili dalla bacheca di WP (es: la tua email, licenza, prezzi e indirizzi). Se vuoi tornare indietro puoi farlo semplicemente tramite%s. Se è stato un errore guarda anche %s.La tua licenza%s è stata disattivata con successo.Il tuo account è stato attivato correttamente con il piano %s.La tua applicazione di affiliazione per %s è stata accettata! Accedi alla tua area di affiliazione a %s.Il tuo account di affiliazione è stato sospeso temporaneamente.Il tuo indirizzo email è stato verificato con successo - SEI UN GRANDE!La tua versione prova è scaduta.%1$s aggiorna ora %2$s per continuare ad usare %3$s senza interruzioni.La tua versione di prova gratuita è scaduta. Puoi continuare ad usare tutte le funzionalità gratuite.La tua licenza è stata cancellata. Se credi sia un errore, per favore contatta il supporto.La tua licenza è scaduta. %1$saggiorna ora %2$sper continuare ad utilizzare %3$s senza interruzioni.La licenza è scaduta. È comunque possibile continuare a utilizzare tutte le funzionalità di %s, ma sarà necessario rinnovare la licenza per continuare a ricevere gli aggiornamenti ed il supporto.La tua licenza è scaduta. Puoi continuare ad usare la versione gratuita %s per sempre.La tua licenza è stata attivata correttamente.La tua licenza é stata disattivata con successo, sei tornato al piano %s.Il tuo nome è stato aggiornato correttamente.Il tuo piano è stato attivato con successo.Il piano è stato cambiato con successo a %s.Il piano è stato aggiornato con successo.La tua sottoscrizione è stata cancellata con successo. La licenza del piano %sscadrà in %s.La versione di prova è stata avviata correttamente.CAPSìattiva una licenza quiAttiva%s non può funzionare senza %s.%s non può funzionare senza il plugin.Attenzionepermetti%s rimanentiAttivazioneannoAPIChiudiDebuggingCongratulazioniBloccatoConnessoScarica l'ultima versioneScarica l'ultima versione gratuitaMensilmenteScadenzaPercorsoInvio emailmeseAnnualeAnnualmenteUna voltaPianoNessuna chiaveVersioni SDKLicenzaSincronizzaSincronizza la licenzaAutoreNon attivoAttivobasato su %sInizia il periodo di prova gratuitoChiudiChiudidatagiornidisattivazione in corsodelega%snon %s mi invierà aggiornamenti di funzionalità e sicurezza, contenuti formativi e offerte.Piano %sFatturato %sMiglioreHeyOpsHey %s,oraoreInstallatoEvvailicenzaSitimsnuova versione Betanuova versionenon verificatoPrezzoPrezziopzionaleVersioneprodottitorna indietrosecsembra che la chiave che hai inserito non risulti nei nostri registri.inviami aggiornamenti di funzionalità e sicurezza, contenuti formativi e offerte.saltaUhmInizia il periodo di prova gratuitosottoscrizionepassa ai siti menzionati sopra stantel'ultima versione %s é quìprovaProva gratuitaEliminaDowngradeModificaNascondiIscrivitiCancella iscrizioneAcquistoMostraSaltaAggiornaAggiornamento%s fafreemius/languages/freemius-hu_HU.mo000064400000060642147600046700013527 0ustar00{ nS< _  "*37Vv~ &-M bl   ,2I!|   )FMa u    ((3:\    x , 9CW_y   a u {      j!5o! !!!!!)"--"["o"'""7""# #)#<#R# Z#d#1#.,$[$A$%=%S%BW%,%% %%% &&"&*& <& G&S& c&o&& &&& &&&&& &&&&''!'('0'L' R'^' g' r''''''-'U'N(m(t( ((((*("(1)+F)*r)&)))).)*)*I*Q* `* k*v** ** **** **++)+0+4+=+ E+Q+ c+n+}++++ ++ ++ + ++ +, ,#, 4, ?,J,_,r,C,,,- -9- ?-L-'S- {-----------*-.*.^I...... . ../" /6,/=c// //-/0!070*Q0|0000&0-091<X1U1K1(72G`2#2%2)2$3)A3k3}3>333$4,4F4b4~4&4*474&5=5[53t555555*61G6y66#6666 7+7B7 W7d77777771 8;8C8W8 h8 t8 8 888C88Q9U9 e9 r9|99 99 9 9 9 9 9 9 9 9 ::~>(b? ?u? @@ ,@ 7@B@E@H@L@U@^@d@ x@ @@@@@@;A3?AsAAA AAAAA$AB -B;B YBdBuB B*BBB C C +C 8C ECPCXC]CfCCCCCCC C DD0DFD_DtD DDD'D DCD 7EAEFE WEaE EE#EEE EEEF 1G?GTG kGvGG#G GGG!H1HFH LHeWHHHHHHIII'I;I LIYIoICI 9JEJNJ'cJJ#J2JJK-KMKPK!lK"KKKKKL L.L2MCM<M#N4NQNYWN'NNN'NO get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 A(z) %1$s fizetős verziója sikeresen telepítve. Kérlek aktiváld, hogy a(z) %2$s minden funkciója elérhető legyen. %3$s"%s" aktiválásának a befejezése most%s licenszA(z) %s egy prémium kiegészítő. A bővítmény aktiválásához előbb egy érvényes licenszt kell vásárolnod.%s értékelés%s értékelés%s csillag%s csillag%s%sAPI←➤FiókFiók információkEseményekAktiválás%s aktiválása%s csomag aktiválása%s funkciók aktiválásaIngyenes verzió aktiválásaLicensz aktiválásaLicensz aktiválása a hálózat minden függő aloldalán.Licensz aktiválása a hálózat minden aloldalán.Kiegészítő bekapcsolásaSikeres aktiválásMásik domain hozzáadásaKiegészítőKiegészítőkCímCím %dAjánlórendszerLicensz elfogadása és aktiválásaMinden kérésMinden típusEngedélyezés és folytatásMennyiségIsmeretlen hiba.Névtelen visszajelzésJelentkezés ajánló partnernekBiztosan törölsz minden Freemius adatot?Biztosan ezt szeretnéd?Automatikus telepítésÁtlagos értékelésNagyszerűSzámlázásSzámlázásBlokkolásBlog IDBodyCégnévVásárolj licenszet mostLicensz vásárlásaNem találod a licensz kulcsod?MégsemTelepítés törléseElőfizetés törlésePróbaidő törléseTörölve%s törlése...Előfizetés törléseLicensz módosításaTulajdonos módosításaCsomag módosításaFelhasználó módosításaPénztárVárosAPI gyorsítótár törléseÁtmeneti frissítési adatok törléseKattints ideKattints ide, ha névtelenül szeretnéd használni a bővítménytTermékekKódKompatibilitás:KapcsolatÍrás az ügyfélszolgálatraKapcsolatKözreműködőkA(z) %s aktiválása nem sikerült.OrszágDátumDeaktiválásLicensz deaktiválásaA(z) %s deaktiválása vagy törlése automatikusan törli az oldalhoz tartozó licenszed is, amit így másik weboldalon tudsz újra aktiválni.A licensz deaktiválása után a prémium funkciók használata nem elérhető, de így tudod másik weboldalon aktiválni ugyanezt a licenszt. Folytatod a deaktiválást?DeaktiválásHibakeresési naplóMinden fiók törléseRészletekNincs még licensz kulcsod?Bővítmény támogatásaElőfizetés módosítása kisebbreLetöltés%s verzió letöltéseFizetős verzió letöltéseTöltsd le a legfrissebb verziótLetöltések száma:EmailEmail címAdd meg az email címet, amit a vásárlás során használtál és újraküldjük a licensz kulcsot.HibaHiba érkezett a szervertől.LejártHátralévő idő: %sTovábbi domainekFájlSzűrőIngyenesIngyenes próbaidőIngyenes verzióFreemius APIFreemius hibakeresésA Freemius SDK nem találja a bővítmény fő fájlját. Kérlek írj az sdk@freemius.com email címre a problémával kapcsolatban.A licenszkezelést és szoftver frissítést a Freemius biztosítjaTeljes névFunkcióVan licensz kulcsod?Hogyan kell feltölteni és aktiválni?Nem tudom tovább fizetniNem értettem, hogy kell használniNem szeretném megosztani veletek az információtJobb %st találtamNincs tovább szükségem ráCsak egy kis időre használtamIDKérlek mondd el, miért %sFontos frissítési információ:Ingyenes verzió telepítése mostTelepítés mostFrissítés telepítése mostBővítmény telepítése: %sSzámlaAktívEz egy ügyfélnek készült weboldal? %s ha szeretnéd inkább elrejteni az admin felületről az érzékeny információkat, mint például email cím, licensz kulcs, árak, számlázási adatok és számlák.Úgy tűnik, hogy a licensz nem aktiválható.Úgy tűnik a licensz kikapcsolása nem sikerült.Úgy tűnik, hogy még mindig a %s csomagban vagy. Ha biztosan előfizettél vagy csomagot váltottál, akkor valószínű a hiba a mi oldalunkon van. Elnézést!Úgy tűnik, hogy a weboldalhoz nem tartozik aktív licensz.Nem ezt kerestemJelentkezz a Béta programbaKulcsHa elmondod mi nem működött, ki tudjuk javítani a leendő felhasználók számára...Ha elmondod az okát, tudunk fejlődni.UtolsóUtolsó frissítésLegfrissebb ingyenes verzió telepítveLegfrissebb verzió telepítveBővebbenHosszLicenszLicensz szerződésLicensz IDLicensz kulcsLicensszel kapcsolatos problémák?Licensz kulcsA licensz kulcs üres.ÖrökLocalhostÜzenetMódModul útvonalTovábbi információNévÚjÚj verzió érhető elHírlevélKövetkezőNemNincs IDBankkártya megadása nem kötelezőRendbenFeliratkozásLeiratkozásIratkozz fel, hogy a(z) %s még jobb lehessen!EgyébTulajdonos email címeTulajdonos IDTulajdonos nevePCI megfelelésPayPal fiók email címeFizetési módokCsomagCsomag IDÍrj nekünk ittKérlek írj nekünk a következő üzenettel:Kérlek add meg a licensz kulcsot, amit emailben kaptál a vásárlásod után:Kérlek add meg a teljes neved!BővítményBővítmény oldalaBővítmény IDBővítmény telepítéseVáltoztatásokLeírásGYIKFunkciók & ÁrakTelepítésEgyéb megjegyzésekVéleményekBővítményekBővítmények és sablonok szinkronizálásaPrémium%s prémium verziója sikeresen aktiválva.Prémium verzióA prémium verzió már aktív.ÁrakAdatkezelési tájékoztatóMűvelet IDFeldolgozás alattTermékekTartományPublikus kulcsLicensz vásárlásaVásárlás folytatásaGyors visszajelzésRendelkezésre állAktivációs email újraküldéseLicensz megújításaLicensz kulcs megújításaKérésekA következő WordPress verzió szükséges:EredménySDKSDK útvonal%s mentéseKépernyőfotókKeresés cím alapjánTitkos kulcsVálaszz országotLicensz kulcs küldéseEgy weboldalas licenszWeboldal IDWeboldalakKihagyás & %sKözvetlen hivatkozásPróbaidő indításaMegyeKüldés & %sElőfizetésÜgyfélszolgálatTámogató fórumAdatok szinkronizálása a szerverrőlKözösségi adószámSzolgáltatási feltételekKöszönjük!Köszönjük %s!A(z) %s hibát generált az oldalonA(z) %s nem működöttA %s nem az elvárásoknak megfelelően működöttA(z) %s nagyszerű, de nekem olyan funkcióra van szükségem, amit nem tudA(z) %s nem működikA(z) %s egyszer csak nem működöttA(z) %s frissítése sikeres volt.SablonSablon váltásSablonokA(z) %s új verziója érhető el.IdőbélyegCímÖsszesenTelepülésPróbaidőTípusKorlátlan licenszKorlátlan frissítésWeboldalak száma: %sFrissítésLicensz frissítéseFrissítések, közlemények, marketing, de semmi SPAM!ElőfizetésTöltsd fel és aktiváld a letöltött verziótFantasztikusFelhasználó vezérlőpultFelhasználó IDFelhasználókÉrtékEllenőrzöttEmail ellenőrzéseRészletek megtekintéseFizetős funkciók megtekintéseFigyelmeztetésNéhány frissítést kapott a %s. %sWeboldal, email és közösségi média statisztikák (opcionális)Köszönjük, hogy a(z) %s szoftverünket választottad! Kérlek add meg a licensz kulcsod: Mire számítottál?Melyik funkcióra van szükséged?Mi a te %s?Mi lenne az elfogadható ár, amit tudnál fizetni?Pontosan mit kerestél?Mi a %s neve?WordPress.org bővítmény oldalSzeretnéd folytatni a frissítést?IgenMinden rendben!Már csak egy lépés van hátra - %sLicensz típusa: %sA(z) %s sikeresen frissítve.A(z) %s licensz kikapcsolása sikerült.A fiókodat sikeresen aktiváltuk a következő csomaggal: %sAz email címedet sikerült ellenőrizni - ez nagyszerű!A licensz kikapcsolásra került. Ha úgy gondolod, hogy ez tévedés volt, kérlek írj az ügyfélszolgálatra!A licensz lejárt. A(z) %s ingyenes verziója továbbra is használható korlátlanul.A licensz sikeresen aktiválva.A licenszedet sikeresen deaktiváltuk, az aktuális csomagod: %sA neved sikeresen frissítettük.Az előfizetés sikeresen aktiválva.Az előfizetés sikeresen módosítva: %s.Az előfizetés sikeresen frissítve.A próbaidőszakodat sikeresen aktiváltuk.IrányítószámAktívFigyelemAktiválásévAPIMégsemHibakeresésGratulálunkBlokkolvaKapcsolódvaLegfrissebb verzió letöltéseA legfrissebb ingyenes verzió letöltéseHaviLejáratÚtvonalEmail küldésehóÉvesÉvesEgyszeriCsomagNincs titkos kulcsSDK verziókLicenszSzinkronizálásLicensz szinkronizálásaSzerzőKiBeIngyenes próbaidőszak indításaMégsemMégsemdeaktiválod%s csomag%s számlázásLegjobbÜdvHoppáÜdv %s!TelepítveJuhuuulicenszWeboldalakúj Béta verzióúj verziónem ellenőrzöttÁrÁrakválaszthatóVerziókérek biztonsági és funkcionális frissítéseket, használati ismertetőket és ajánlatokat.ugrásHmmpróbaidő indításaelőfizetésváltaszpróbaidőPróbaidőTörlésVáltás kisebb csomagraSzerkesztésElrejtFeliratkozásLeiratkozásVásárlásMutasdUgrásFrissítésVáltás nagyobb csomagrafreemius/languages/freemius-he_IL.mo000064400000112600147600046700013467 0ustar00|S%1 W co6v_b#   ")1:BHK & -, Z o y     5   ! !'#!K! d! q!{!!!2"E""a""2"!"a"X#j######## ####$ $$ 1$Y;$$$ $$$$($1%%:%:`%%%%% % %%% % & &&,& & &&&' '&'<'E'Y'x' ''7( =(K(YO(a( ))1) 9) G);U))))* * * **j*@+ O+Y+3b++~+U),,,,),-,'-S;--'--7-g .r.x.. .... .1..'/OV//A&0yh0201a511B1,1 2 2 2*2H2 a2l2s2 {2 222422 233 33 3&3 -3 93E3_3 d3q3u33 3333%3+34 +4 94/F4v4mz4444 4 5 5 5)+5U5r54{5555(566-36a6Uu6617[7"8A8H8 X8b8(q8*8"818+9*F9&q9999.999:%: 4: ?:J:S:c:u: ~::::W: ;;(;C;J;N;W;_; o;{; ;5;l;&;<b<q< <<<<< <<&<L=fN== === = == = > #>0>A>>n?)? ? ?\?+@@@S@Cr@@@@-rAA AA'AMAG0B xBBBBBBEBBB C C/C6C*ECpC*xC^CD DDdD{D DD DDDiDWGG&HZ5H.H.H9HZ(I3I<IPIUEJJK6K(KGK#K)L$AL)fLLL;L6L>2MqMwMMM$MMN#N?N&]N*NNNN3N1OFO\OtOO*O1OPP#2PVPrPP PPPP PPNQQQpQQQQQ1Q R!R 1R >R IRVR nRCzRRQRS %S/SJS ]SiS xS S S S S S S S S S S#X(YGY WY cY6oYY_[Z5ZZ [ [%[ 5[ C[O[ _[ i[Hw[[ [[ [ [ [\$\C\O[\8\\ \ ]"]7]J]Q]5Y] ] ] ]]']$]^$^6^P^Y^._BH_+__2_.`a6`%````` aa#a5a :a6Fa}aaaaaaDb\brbbbb(b1b%c 6cCcHcYc acoc c#c c c c cccd d$d d e1(eZe neye.e'e ee fffYfeg kg#vg gg g;g h h hi(i>i Xi$eijii jj3$j!Xj~zjUj#Oksk7k@kJlPlnll' m2m7;mgsm m+m6nJn ^n$nnn:n.nO-o}o:oy8p6ppaqhqmlq=qrr7r1Or&r rr r rrr s4sPs_sssws ~ss s s sssssst#t>tMtRtWt%gt@t#tt u/u AumLuuuuuuuv)$vNvkv4zv v`v>wZw&bwBwwmwUxFy[ey#yyy z z z 6zAzYz xzzz zzz.z{* { K{X{ t{ { {{{{{{{6{W5||||||| ||}}.}5@}v}4}+~ =~ ^~l~~~ ~ ~~&~kft &) =I R] sG<ށA] n{.CL%Ƀ-x  DŽ%ԄGBB  E  "CZmvS8$)=LdR ‡އ'6i>W"6#Zm7͉މ&5QV(_M֊/X:$Ӌ5^I8ތZ3r<{__KZ'>Ώ /-']. ϐ&ڐACW ` ny   ۑ  # , :E^ o| ђ y  ēғٓ   *3u7 ܔ$ (1:A IT[bk t%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is the new owner of the account.%s minimum payout amount.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s tracking cookie after the first visit to maximize earnings potential.APIAccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre you sure you want to delete all Freemius data?Are you sure you want to proceed?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBillingBlockingBlog IDBodyBusiness nameCan't find your license key?CancelCancel InstallationCancel SubscriptionCancel TrialCancelledCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanCheckoutCityClear API CacheClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDelegate to Site AdminsDelete All AccountsDetailsDon't have a license key?Donate to this pluginDownloadDownload %s VersionDownload the latest %s versionDownload the latest versionDownloadedDue to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.EmailEmail addressEndEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used for the upgrade below and we will resend you the license key.ErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.FreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFull nameFunctionGet commission for automated subscription renewals.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.In %sInstall Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInvalid module ID.InvoiceIs ActiveIt looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's a temporary %s - I'm troubleshooting an issueIt's not what I was looking forJust letting you know that the add-ons information of %s is being pulled from an external server.KeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense KeyLicense keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerMessageMethodMobile appsModuleModule PathModule TypeMore information about %sNameNetwork UserNewNew Version AvailableNewer Version (%s) InstalledNewsletterNextNoNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Opt InOpt OutOtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.PayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium versionPremium version already active.PricingPrivacy PolicyProcess IDProcessingProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicenseQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRequestsRequires WordPress VersionResultSDKSDK PathSave %sScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSimulate Network UpgradeSingle Site LicenseSite IDSitesSkip & %sSlugSocial media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart TrialStart my free %sStateSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a new version of %s available.This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.TimestampTitleTotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser IDUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWebsite, email, and social media statistics (optional)What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?Would you like to proceed with the update?YesYes - %sYou already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou do not have a valid license to access the premium version.You have a %s license.You have successfully updated your %s.You might have missed it, but you don't have to share any data and can just %s the opt-in.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,interjection expressing joy or exuberanceYee-hawlike websitesSitesmillisecondsmsnot verifiednounPricenounPricingproduct versionVersionsecondssecsend me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialswitchingthe latest %s version heretrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Vova Feldman , 2017-2018,2022 Language-Team: Hebrew (Israel) (http://app.transifex.com/freemius/wordpress-sdk/language/he_IL/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: he_IL Plural-Forms: nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2; X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 השלם הפעלת "%s" עכשיוההרחבה %s נרכשה בהצלחה.%s התקנות%s Licensesלפני %s%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s הינו הבעלים החד של חשבון זה.%s minimum payout amount.%s ומעלהדרוג %s%s דרוגים%s שניותכוכב %s%s כוכביםפעם %s%s פעמים%s tracking cookie after the first visit to maximize earnings potential.APIחשבוןפרטי חשבוןפעולותהפעלהActivate %sהפעל חבילה %sהפעלת גירסה חינאמיתהפעלת רישיוןהפעלת רישיון על כל האתרים התלויים והעומדים.הפעלת רישיון על כל האתרים ברשת.הפעל את ההרחבהActivatedהרחבות עבור %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.כתובתכתובת %sAffiliateאפיליאציהAfter your free %s, pay as little as %sהסכמה והפעלת רישיוןכל הבקשותכל הסוגיםאפשר\י והמשכ\יסכוםהורדה והתקנה אוטומטית של %s (גרסה בתשלום) מ-%2$s תתחיל בעוד %3$s. אם ברצונך לבצע את ההתקנה ידנית - לחץ על כפתור הביטול עכשיו.פידבק אנונימייישום על כל האתרים התלויים והעומדים.יישום על כל האתרים ברשת.Apply to become an affiliateAre you sure you want to delete all Freemius data?האם את/ה בטוח רוצה להמשיך?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.עדכן אוטומטית בעוד %sהתקנה אוטומטיתדירוג ממוצעאדירBecome an affiliateבילינגBlockingמזהה בלוגBodyשם עסקהאם אינך מוצא את מפתח הרישיון?בטלבטל התקנהבטל מנויביטבוטלביטול הניסיון יחסום מייד את הפיטצ'רים שהינם בתשלום. האם ברצונך בכל זאת להמשיך?שינוי רישיוןעדכון בעלותשינוי חבילהCheckoutעירניקוי מטמון ה-APIClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dמוצריםCodeCompatible up toContactצור קשריצירת קשרתורמיםלא ניתן להפעיל את %s.מדינהCron Typeתאריךכיבוישיחרור רישיוןביטול הרישיון יחסום את כל הפיטצ'רים שבתשלום אך יאפשר להפעיל את הרישיון על אתר אחר. האם תרצו להמשיך בכל זאת?דיאקטיבציהDebug Logהאצלה למנהלי האתריםמחיקת כל החשבונותפרטיםהאם אין ברשותך מפתח רישיון?תרום לתוסףהורדההורד גרסת %sהורד\י את גרסת ה-%s העדכניתהורד את הגרסה האחרונהDownloadedDue to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.דוא"לכתובת דוא"לEndEnter the domain of your website or other websites from where you plan to promote the %s.הזן את כתובת הדואל שאיתה שידרגת כדי לקבל את הרישיון שוב.שגיאההוחזרה שגיאה מהשרת:פג תוקףפג תוקף בעוד %sExtra DomainsExtra domains where you will be marketing the product from.קובץפילטרFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.חינםניסיון חינםגירסה חינאמיתFreemius APIניפוי תקלות פרימיוסFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.מצב פרימיוסFull nameפונקציהGet commission for automated subscription renewals.האם ברשותך רישיון?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.איך להעלות ולהפעיל?How will you promote us?אני לא יכול/ה להמשיך לשלם על זהלא הצלחתי להבין איך לגרום לזה לעבודאני לא אוהב את הרעיון של שיתוף מידע איתכםמצאתי %s יותר טובשידרגתי את החשבון שלי אבל כשאני מנסה לבצע סנכרון לרישיון החבילה נשארת %s.I no longer need the %sI only needed the %s for a short periodמזההIf you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.בעוד %sהתקן גרסה חינאמית עכשיוהתקן עדכון גרסה חינאמית עכשיוהתקן עכשיוהתקן עדכון במיידימזהה המודול לא תקני.חשבוניתהאם פעילנראה שלא ניתן להפעיל את הרישיון.נראה שניתוק הרישיון נכשל.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.נראה לאתר עדיין אין רישיון פעיל.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.זה %s זמני - אני מנסה לפתור בעיהחיפשתי משהו אחרJust letting you know that the add-ons information of %s is being pulled from an external server.Keyאנא שתפ\י מה לא עבד כדי שנוכל לתקן זאת עבור משתמשים עתידיים...אנא שתף את הסיבה כדי שנוכל להשתפר.Lastעודכן לאחרונהרישיון אחרוןגרסה חינאמית עדכנית הותקנההגרסה האחרונה הותקנהLearn moreLengthרישיוןLicense Keyמפתח רישיוןמפתח הרישיון ריק.לכל החייםLike the %s? Become our ambassador and earn cash ;-)Load DB Optionשרת לוקאליLogLoggerהודעהMethodMobile appsמודולModule Pathסוג מודולמידע נוסף אודות %sשםמשתמש רשתחדשיש גרסה חדשהגרסה חדשה (%s) הותקנהניוסלטרNextלאאין מזההNo commitment for %s - cancel anytimeללא התחייבות ל-%s ימין - בטלו בכל רגע!לא נדרש כרטיס אשראיללא תפוגהNon-expiringNone of the %s's plans supports a trial period.אוקייOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Opt InOpt Outאחרמייל הבעליםמזהה הבעליםשם הבעליםעומד בתקן PCIPaid add-on must be deployed to Freemius.PayPal account email addressתשלומיםPayouts are in USD and processed monthly via PayPal.חבילההחבילה %s אינה קיימת, לכן, לא ניתן להתחיל תקופת ניסיון.תוכנית %s אינה תומכת בתקופת ניסיון.Plan IDאנא צור איתנו קשר כאןאנא צור איתנו קשר יחד עם ההודעה הבאה:נא להוריד את %s.אנא הזן את הרישיון שקיבלת לתיבת הדואל שלך לאחר השלמת הרכישה.Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).נא לבצע את הצעדים הבאים להשלמת השידרוגPlease provide details on how you intend to promote %s (please be as specific as possible).נא למלא את שמך המלא.תוסףעמוד התוסףPlugin IDהתקנת תוסףלוג שינוייםתיאורשאלות נפוצותפיטצ'רים ומחיריםהתקנההיערות נוספותביקורותתוספיםPlugins & Themes SyncPremiumPremium %s version was successfully activated.גירסת פרימיוםהגרסה בתשלום כבר פעילה.מחירוןמדיניות פרטיותProcess IDProcessingמוצריםProgram SummaryPromotion methodsפרובינציהמפתח פומביקניית רישיוןQuotaשליחה חוזרת של מייל האקטיבציהRefer new customers to our %s and earn %s commission on each successful sale you refer!חידוש רישיוןRequestsRequires WordPress VersionResultSDKמיקום SDKשמירת %sScheduled Cronsצילומי מסךחפש לפי כתובתמפתח סודיSecure HTTPS %s page, running from an external domainנראה שיש תקלה זמנית המונעת את ביטול הניסיון. אנא נסו שוב בעוד כמה דקות.נראה שיש לך את הגרסה האחרונה.בחר מדינהשליחת מפתח רישיוןSet DB Optionסמלוץ עדכון לרשתרשיון לאתר אחדמזהה אתראתריםדלג ו%sמזהה כתובתSocial media (Facebook, Twitter, etc.)מצטערים על חוסר הנעימות, אנחנו כאן כדי לעזור אם תאפשר\י זאת.Sorry, we could not complete the email update. Another user with the same email is already registered.Startהתחל תקופת ניסיוןהתחל את %s הניסיון שלימחוז/מדינהSubmit & %sמנויתמיכהפורום תמיכהסנכרון מידע מהשרתח.פ.תנאי השירותThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.אנו מודים לך על היותך כמשתמש של %s!אנו מודים לך על השימוש במוצרים שלנו!תודה רבה!תודה %s!תודה על אישור ביצוע החלפת הבעלות. הרגע נשלח מייל ל-%s כדי לקבל אישור סופי.ה%s הרס לי את האתרה%s לא עבדה%s לא עבד כמצופהThe %s is great, but I need specific feature that you don't supportה%s לא עובדה%s הפסיק פתאום לעבודתהליך ההתקנה התחיל ויכול לקחת מספר דקות לסיום. אנא המתינו בסבלנות עד לסיום מבלי לרענן את הדפדפן.The upgrade of %s was successfully completed.תבניתהחלפת תֵמָהתבניותיש גרסה חדשה עבור ה%s.התוסף לא סומן כתואם לגרסת הוורדפרס שלך.תוסף זה לא נבדק עם גרסת הוורדפרס שלך.TimestampכותרתTotalכפרניסיוןסוגUnable to connect to the filesystem. Please confirm your credentials.רשיונות ללא הגבלהעדכונים ללא הגבלהUnlimited commissions.עד %s אתריםעדכןעדכון רישיוןעדכונים, הכרזות, הודעות שיווקיות, ללא דואר זבלשדרגהעלה\י והפעיל\י את הגרסה שהורדתישמזהה משתמשמשתמשיםValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.מאומתאמת כתובת דוא"לגרסה %s הושקה.פרטים נוספיםצפה בפיטצ'רים שבתשלוםWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWebsite, email, and social media statistics (optional)למה ציפית?איזה פיטצ'ר?מה ה%s שלך?מה המחיר שכן תרגיש\י בנוח לשלם?מה חיפשת?What's the %s's name?Where are you going to promote the %s?האם ברצונך להמשיך עם העידכון?כןYes - %sהניסיון כבר נוצל בעבר.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.את\ה מסודר!You are already running the %s in a trial mode.You are just one step away - %sאין ברשותך רישיון בר תוקף לשימוש בגרסת הפרימיום.יש לך רישיון %s.עידכנת בהצלחה את ה%s.אולי פספסת את זה אבל אינך חייב\ת לשתף כל מידע איתנו, ביכולתך %s על שיתוף המידע.חבילת ההרחבה %s שודרגה בהצלחה.תקופת הניסיון החינמית של %s בוטלה בהצלחה.חשבונך הופעל בהצלחה עם חבילת %s.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!תקופת הניסיון שלך הסתיימה. הפיטצ'רים החינאמיים עדיין ניתנים לשימוש.רשיונך בוטל. אם לדעתך זו טעות, נא ליצור קשר עם התמיכה.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.הרישיון הופעל בהצלחה.רישיונך נותק בהצלחה, חזרת לחבילת %sשמך עודכן בהצלחה.החבילה עודכנה בהצלחה אל %s.החבילה שודרגה בהצלחה.הניסיון שלך הופעל בהצלחה.מיקוד / תא דוארמעולה%s לא יכול לעבוד ללא %s.ההרחבה %s אינה יכולה לפעול ללא התוסף.לתשמות לבךאפשרנשארו %sמפעילשנהAPIסגירהדיבוגמזל טובחסוםמחוברהורד גרסה אחרונהחודשיתפוגהנתיבשולח דוא"לחודשיםשנתישנתיפעם אחתחבילהאין מפתח סודיגרסאות SDKרישיוןסינכרוןסינכרן רישיוןAuthorכבוידלוקמבוסס על %sהתחלת ניסיון חינםסגירהסגירהdeactivatingהאצל%sאל%2$s תשלחו לי עדכוני אבטחה, פיטצ'רים, תוכן חינוכי, ומידע על מבצעים.חבילה %sמחוייב על בסיס %sהכי טובהייאופסהיי %s,ישששאתריםmsלא מאומתמחירמחירוןגרסהsecתשלחו לי עדכוני אבטחה ופיטצ'רים, תוכן חינוכי, ומידע אודות מבצעים.דלגאממהתחל תקופת ניסיוןswitchingגרסת ה-%s האחרונה כאןניסיוןמחקשנמךערוךהסתרOpt InOpt Outרכישההצגדלגעדכןשדרגלפני %sfreemius/languages/freemius-fr_FR.mo000064400000133364147600046700013517 0ustar00T A S %#! I! U!a!h!6{!!_g"#"" # # #'#.#6#?#G#@P#H##O#=$A$`$$$$$ $$$$$&%-)%W% l%v%%%%%5%%% & &' &H& a& n&x&o&&'''"''2(!A(ac(0(()).)6)J)R)[)c) h)v) ))))) _*j*~* * * ***Y*>+M+ ^+j+s+x++(+1+%+:",],b,s,{, , ,,, ,, ,,x,g- - ..+.?.tG..../ //>/ Z/e/[/fY00 00Y0a21111 1 1;122&2%3 *3 53 B3O3j^33 33334~34U45$5=5)X5-55S56'06X6M[676g6pI777y7T8m8 8888 88 819.:9Oi99A9:y{::a;w;B{;,;; ; ; <(< A<L<S<[< m< y<<<4<< <<<<= = '=3= := F=R=l= q= ~===!== ====%=+#>O> g> u>/>>m>~$????? ?? ? ?)@,@I@4R@@5@(@@@- A8AULAA1kB~BC[9DDDD DD(D* E"8E1[E+E*E&EN FZFbFxF.F)FFF GG G (G 3G>GGGWGiG rG}GGGGWG H"H9HBH]HdHhHqHyH HH H5HsHl\I&III JJ7JPJdJlJJ JJ&JLJfKxK ~KKK K KK KK KKLL/1MaM)M M M\MN3NFNCeNNNNdeO-OO O PP'1PMYPGP PPPQ QQEQ[QnQQQQQ*QQ*Q^RyRRRdRR RS !S.SASiISWS" TB.T6qTT TT-T U(U&>UeUUU$UMUU/VAVoaV>VW&'WZNWTWRW.QX.X9XZX3DY<xYbYPZUiZ_Z[K[(\G/\#w\)\$\U\)@]j]|];]6]> ^K^Q^l^^$^^^^_&7_*^_7____3`C`X`n```*`1`a0a#Dahaaa aaaa a bNbcbbbbbb1bc'c;c Kc Wc dc oc|c ccQcc d dd9d?d Rd^d md wd d d d d d d d d dhi"Uj!xjj j jj>jkbk*Blml l l ll l lllilZImmWmn n)n2n9nLnTn \ngn}nnn1n1n0oCoKo[opoooBooo o p* p8pUpipxp~p qqq&q- r!;rF]r!r}rXDs#ss s ss ttt#t(t7tVt+jtttt yuuuuuuuunvvvvvvv$v/ w2~M~Og~~~T~ #1 BOxc܀ KOo]!]*0ɂ2-gI1˃P;Qq((;7d$ֆ/ ; C2M9U5 OsM{Ɋъ/*GV_gz?- ,6:AI"Ru#ތ#54 j  6>$AR4b$ %ɏ  57D| PT1S1XX:C~]/n$!( ;H _ iuy  Z)$N7V5ėԗ #3GPf} ˜xY#r ƙʙ *>8w-{0כ"+HW_z *[o  ɝ՝ݝ.!&H-ڟ(" *z5Ƞ&Y c,{}R3Т  (.HawG٣ !+17=NYSǤ /3 c1p ƥ LV%h" æxͦjF0SP6&Ҩ*'Ros.|c0%VuF#j"d<b)/.=^k6D?wgwkY$qP-(4W]( ޲ #* E S]l  "+ &=BIPYay  ĴѴ q Ƶɵ ڵ * 5@_e v  ɶ ж ݶ %s to access version %s security & feature updates, and support.%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is the new owner of the account.%s minimum payout amount.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s to access version %s security & feature updates, and support.%s tracking cookie after the first visit to maximize earnings potential.%s's paid features%sClick here%s to choose the sites where you'd like to activate the license on.APIASCII arrow left icon←ASCII arrow right icon➤AccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre you sure you want to delete all Freemius data?Are you sure you want to proceed?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBillingBlockingBlog IDBodyBusiness nameBuy a license nowBuy licenseCan't find your license key?CancelCancel %s & ProceedCancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site.Cancel %s?Cancel InstallationCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanCheckoutCityClear API CacheClear Updates TransientsClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site.Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDelegate to Site AdminsDelete All AccountsDetailsDon't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support.Don't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload the latest %s versionDownload the latest versionDownloadedDue to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.During the update process we detected %d site(s) that are still pending license activation.During the update process we detected %s site(s) in the network that are still pending your attention.EmailEmail addressEndEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used for the upgrade below and we will resend you the license key.ErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.FreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFull nameFunctionGet commission for automated subscription renewals.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf you click it, this decision will be delegated to the sites administrators.If you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.Important Upgrade Notice:In %sIn case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?Install Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid module ID.Invalid site details collection.InvoiceIs ActiveIt looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's not what I was looking forJust letting you know that the add-ons information of %s is being pulled from an external server.KeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense KeyLicense keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerMessageMethodMigrate Options to NetworkMobile appsModuleModule PathModule TypeMore information about %sNameNetwork BlogNetwork UserNewNew Version AvailableNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license.Opt InOpt OutOpt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.PayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.Please provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProceedProcess IDProcessingProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicenseQuick FeedbackQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRenew your license nowRequestsRequires WordPress VersionResultSDKSDK PathSave %sScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes.Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSimulate Network UpgradeSimulate Trial PromotionSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSocial media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart TrialStart my free %sStateSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a %s of %s available.There is a new version of %s available.This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.TimestampTitleTotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser IDUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress.org Plugin PageYesYes - %sYou already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.You do not have a valid license to access the premium version.You have a %s license.You have successfully updated your %s.You might have missed it, but you don't have to share any data and can just %s the opt-in.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your subscription was successfully cancelled. Your %s plan license will expire in %s.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,interjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew versionnot verifiednounPricenounPricingproduct versionVersionsecondssecskipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Boris Colombier , 2018 Language-Team: French (France) (http://app.transifex.com/freemius/wordpress-sdk/language/fr_FR/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: fr_FR Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2; X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 %s pour accéder aux mises à jour de sécurité et de fonctionnalités de la version %s, et au support.Compléter "%s" Activer MaintenantL'Add-on %s a bien été acheté.%s Installations%s LicencesIl y a %s%s et ses add-onsCommission de %s quand un client achète une nouvelle licence.La période d'essai du %s a bien été annulé. L'add-on a été désactivé car il ne fonctionne qu'avec la version premium. Si vous souhaitez l'utiliser ultérieurement, vous devrez acheter une licence.%sest un add-on pour la version premium. Vous devez acheter une licence avant d'activer le plugin.%s est le nouveau propriétaire du compte.Montant de paiement minimum %s.%s ou plus%s notation%snotations %s sec%s étoile%s étoiles%s fois%s fois%s pour permettre les mises à jour de sécurité et de fonctionnalités de la version %s, et le support.Cookie de tracking de %s après la première visite pour maximiser les potentiels de gain.Fonctionnalités payantes de %s%sCliquez ici %s pour choisir les sites sur lesquels vous souhaitez activer la licence.API←➤CompteDétails du compteActionsActiverActiver %sActiver la formule %sActiver les fonctionnalités %sActivez la version gratuiteActiver la licenceActiver la licence sur tous les sites en attente.Activer la licence sur tous les sites du réseau.Activer cet add-onActivéAdd Ons pour %sAdd Ons du module %sAjouter une autre adresseAdd-OnAdd-OnsLes add-ons doivent être déposés sur WordPress.org ou Freemius.AdresseAdresse ligne %dAffiliationAffiliationAprès vos %s gratuits, payez seulement %sValider & Activer la licenceToutes les demandesTous les typesAutoriser & ContinuerÉventuellement, vous pouvez l'ignorer pour l'instant et activer la licence plus tard, sur votre page de compte du réseau %s.MontantUn téléchargement et une installation automatique de %s (version premium) de %s va commencer dans %s. Si vous voulez le faire manuellement, cliquez sur le bouton d'annulation maintenant.Commentaire anonymeActiver sur tous les sites en attente.Effectuer sur tous les sites dans le réseau.Postuler pour devenir un affiliéÊtes-vous sûr de vouloir supprimer toutes les données de Freemius ?Êtes-vous de vouloir continuer ?Comme nous bloquons sur 30 jours pour les remboursements éventuels, seules sont payées les commissions de plus de 30 jours.L'installation automatique ne fonctionne que pour les utilisateurs qui se sont inscrits.Renouvellements automatique dans %sInstallation automatiqueNote moyenneFormidableDevenir un affiliéFacturationBloquantBlog IDBodyRaison socialeAcheter une licence maintenantAcheter une licenceVous ne trouvez pas votre clef de licence ?AnnulerAnnuler %s et poursuivreAnnuler %s - Je n'ai plus besoin de mises à jour de sécurité et de fonctionnalités, ni de support pour %s parce que je n'ai pas l'intention d'utiliser le %s sur ce site, ou tout autre site.Annuler %s ?Annuler l'installationAnnuler l'abonnementAnnuler la période d'essaiAnnuléAnnulation de %sAnnulation de %s...Annuler votre abonnementAnnuler la période d'essai va immédiatement bloquer les fonctionnalités premium. Souhaitez-vous continuer ?Changer la licenceChangement De PropriétaireChanger de formulePaiementVilleVider le cache APIVider les transients de mise à jourCliquer ici pour utiliser le plugin anonymementCliquez pour voir les avis avec une notation de %sCliquez pour voir la capture d'écran %d en pleine tailleProduitsCodeCompatible jusqu'àContactContacter l'AssistanceContactez NousContributeursImpossible d'activer %s.PaysType de CronDateDésactiverDésactiver la licenceDésactiver ou désinstaller le %s désactivera automatiquement la licence, que vous pourrez utiliser sur un autre site.Désactiver la licence bloquera toutes les fonctionnalités premium mais vous permettra d'activer la licence sur un autre site. Êtes-vous sûr de vouloir continuer ?DésactivationDebug LogDéléguer aux administrateurs du siteSupprimer tous les comptesDétailsNe pas annuler %s - Je veux toujours recevoir les mises à jour de sécurité et de fonctionnalités, ainsi que d'être en mesure de contacter le support.Vous n'avez pas de clef de licence ?Faire une donation pour ce pluginRétrograder votre formuleTéléchargementTélécharger la version %sTélécharger la dernière version %sTélécharger la dernière versionTéléchargéSuite à une violation de nos conditions d'affiliation, nous avons décidé de bloquer temporairement votre compte d'affilié. Si vous avez la moindre question, merci de contacter le support.Durant le processus de mise à jour nous avons détecté %d site(s) toujours en attente d'activation de la licence.Durant le processus de mise à jour nous avons détecté %s site(s) dans le réseau que vous devez vérifier.EmailAdresse emailFinIndiquez l'adresse de votre site ou d'autres sites sur lesquels vous pensez faire la promotion du %sIndiquez ci-dessous l'adresse email que vous avez utilisez pour la mise à jour et nous allons vous renvoyer le code de la licence.ErreurUne erreur a été reçu depuis le serveur :ExpiréExpire dans %sAdresses supplémentairesAdresses supplémentaires depuis lesquelles vous ferez la promotion du produit.FichierFilterPour être en accord avec les directives de WordPress.org, avant que nous commencions la période d'essai, nous vous demandons de nous permettre de récupérer votre nom d'utilisateur et des informations non sensibles du site afin de permettre au %s de communiquer avec %s pour vérifier les mises à jour et valider votre période d'essai.GratuitEssai gratuitVersion gratuiteAPI FreemiusDébuggage FreemiusLe SDK Freemius ne trouve pas le fichier principal du plugin. Merci de contacter sdk@freemius.com en indiquant l'erreur.État de FreemiusNom completFonctionObtenez des commissions pour les renouvellements automatiques d'abonnement.Vous avez une clef de licence ?Dites, savez-vous que %s propose un système de affiliation ? Si vous aimez le %s vous pouvez devenir notre ambassadeur et gagner de l'argent !Que pensez-vous de %s ? Testez nos %s fonctionnalités premium avec %d jours d'essai gratuit.Comment téléverser et activer ?Comment allez-vous faire de la promotion ?Je ne peux plus payer pour çaJe ne comprends pas comment le faire fonctionnerJe ne veux pas partager mes informations avec vousJ'ai trouvé un meilleur %sJ'ai mis à jour mon compte mais quand j'essaie de synchroniser la licence, la formule est toujours %s.Je n'ai plus besoin du %sJe n'ai besoin de %s que pour une courte périodeIDSi vous cliquez, cette décision sera déléguée aux administrateurs des sites.Si vous avez un instant, merci de nous indiquer pourquoi %sSi vous voulez transférer la propriété du compte de %s à %s cliquez sur le bouton Changement De PropriétaireSi vous voulez utiliser le %s sur ces sites, merci d'indiquer votre clé de licence ci-dessous et de cliquer sur le bouton d'activation.Information importante de mise à jour :Dans %sDans le cas où vous n'avez PAS l'intention d'utiliser ce %s sur ce site (ou tout autre site) - voulez-vous aussi annuler le %s ?Installer la version gratuite maintenantInstaller la dernière mise à jour gratuite maintenantInstaller maintenantInstaller la mise à jour maintenantInstallation du plugin : %sID du module non valide.Récupération des détails du site non valide.FactureEst actifIl semble que la licence ne puisse être activée.Il semble que la désactivation de la licence a échoué.Il semble que vous ne soyez plus en période d'essai donc il n'y a rien à annuler :)Il semble que vous soyez encore sur la formule %s. Si vous avez mis à jour ou changer votre formule, le problème est probablement de votre côté - désolé.Il semble que votre site n'ait pas de licence active.Il semble que l'un des paramètres d'authentification soit faux. Veuillez mettre à jour votre Public Key, votre Secret Key ainsi que vote User ID et essayez à nouveau.Ce n'est pas ce que je rechercheSachez que les informations de l'add-ons de %s sont issus d'un serveur externe.ClefMerci de nous indiquer ce qui ne fonctionne pas afin que nous puissions le corriger pour les futurs utilisateurs...S'il vous plait, dites nous pourquoi afin que nous puissions nous améliorer.DernierDernière mise à jourDernière licenceLa dernière version gratuite a été installéDernière Version InstalléeEn savoir plusLongueurLicenceContrat de licenceClef de licenceClef de licenceLa clé de licence est vide.À vieVous aimez %s ? Devenez notre ambassadeur et gagnez du cash ;-)Chargement des options de la base de donnéesLocalhostLogLoggerMessageMéthodeMigrer les options vers le réseauApplications mobilesModuleChemin d'accès du moduleType de modulePlus d'informations à propos de %sNomRéseau de BlogRéseau d'UtilisateurNouveauUne nouvelle version est disponibleLa nouvelle version gratuite ( %s ) a été installéNouvelle Version (%s) InstalléeNewsletterSuivantNonID manquantPas d'engagement durant %s - annuler quand vous voulezPas d'engagement durant %s jours - annuler quand vous voulez !Pas besoin de carte bancairePas d'expirationSans expirationAucune formule du %s ne propose de période d'essai.O.KUne fois la licence expirée vous pourrez toujours utiliser la version gratuite mais vous n'aurez PAS accès aux fonctionnalités de %s.Une fois votre licence expirée, vous ne pourrez plus utiliser le %s, sauf si vous l'activez à nouveau avec une licence premium valide.InscriptionDésinscriptionInscrivez-vous pour améliorer "%s" !AutreEmail du propriétaireID du propriétaireNom du propriétaireCompatible PCILes add-ons payant doivent être déposés sur FreemiusAdresse email du compte PayPalPaiementsLes paiements se font en Dollars US et sont effectués mensuellement via PayPal.FormuleLa formule %s n'existe pas, il n'est pas possible de commencer une période d'essai.La formule %s ne propose pas de période d'essai.ID de la formuleMerci de nous contacter iciMerci de nous contacter avec le message suivant :Merci de télécharger %s.Merci d'indiquer le code de licence que vous avez reçu par email juste après l'achat :N'hésitez pas à indiquer des statistiques pertinentes concernant votre site ou vos réseaux sociaux telles que le nombre de visiteurs mensuel, le nombre d'abonnés, de followers, etc... (C'est informations resteront confidentielles)Merci de suivre ces étapes pour finaliser la mise à jourMerci de nous indiquer si vous souhaitez que nous vous contactions pour les mises à jour de sécurité et de fonctionnalités, du contenu instructif et des offres spéciales :Veuillez noter que nous ne serons pas en mesure de garantir le maintien des prix actuels pour les renouvellements/nouveaux abonnements après une annulation. Si vous choisissez de renouveler l'abonnement manuellement à l'avenir, après une augmentation de prix, qui se produit généralement une fois par an, le prix mis à jour vous sera facturé.Merci d'indiquer en détail comment vous allez faire la promotion du %s (en étant aussi précis que possible)Merci d'indiquer vos prénom et nom.PluginSite Web du pluginID du pluginInstallation du PluginChangelogDescriptionFAQFonctionnalités & TarifsInstallationAutres InformationsCommentairesLe plugin est un "Serviceware" ce qui veut dire qu'il n'a pas de version premium de code.PluginsSynchronisation des plugin et des thèmesPremiumLa version premium de %s a été activée avec succès.La version premium de l'add-on est déjà installée.Version premiumVersion premium déjà active.TarifsPolitique de confidentialitéPoursuivreID du processusTraitement en coursProduitsSommaire du programmeMéthodes de promotionRégionClef publiqueAcheter une licenceCommentaires rapidesQuotaRenvoyer l'email d'activationParrainez des nouveaux clients pour notre %s et gagnez une commission de %s sur chaque vente réussie que vous affiliez.Renouvelez votre licenceRenouvelez votre licence maintenantDemandesVersion de WordPress requiseRésultatSDKChemin d'accès du SDKÉconomisez %sCrons programmésCaptures d'écranRecherche par adresseClef secrêtePage %s sécurisée HTTPS, s'exécutant sur un domaine externeIl semble que nous ayons un problème temporaire avec l'annulation de votre abonnement. Merci de réessayer dans quelques minutes.Il semble que nous ayons un problème temporaire pour annuler votre période d'essai. Merci de réessayer dans quelques minutes.Il semble que vous ayez la dernière version.Choisir le paysEnvoyer le code de la licenceMise en place des options de la base de donnéesSimuler la mise à jour du réseauSimuler la promotion d'essaiLicence 1 siteSite IDSite ajouté avec succès.SitesPasser & %sSlugRéseaux sociaux (Facebook, Twitter, etc.)Désolé pour le dérangement et nous sommes là pour vous aider si vous nous le permettez.Désolé, nous ne pouvons pas mettre à jour l'email. Il existe déjà un autre utilisateur avec cette adresse.DébutEssai gratuitCommencer ma %s gratuiteÉtatEnvoyer & %sInscriptionSupportForum de SupportSynchronisation des données depuis le serveurCode TVAConditions générales de serviceMerci d'avoir postulé à notre programme d'affiliation, malheureusement, nous avons décidé pour le moment de décliner votre dossier. Merci d'essayer à nouveau d'ici 30 jours.Merci d'avoir postulé à notre programme d'affiliation, nous regarderons votre dossier durant les 14 prochains jours et nous reviendrons vers vous avec d'autres informations.Merci beaucoup d'utiliser %s et ses add-ons !Merci beaucoup d'utiliser %s !Merci beaucoup d'utiliser nos produits !Merci !Merci %s !Merci pour la confirmation du changement de propriétaire. Un email vient d'être envoyé à %s pour la validation finale.Le %s a cassé mon siteLe %s n'a pas fonctionnéLe %s n'a pas fonctionné comme prévuLe %s est bien mais j'ai besoin de fonctionnalités spécifiques que vous ne proposez pasLe %s ne fonctionne pasLe %s a soudainement arrêté de fonctionnerL'installation a commencé et peut prendre quelques minutes pour se finir. Merci de patienter jusqu'à ce qu'elle soit terminée - veuillez ne pas rafraichir cette page.Le package du plugin à télécharger ne contient pas de dossier avec le bon slug et iln'a pas été possible de le renommer.La mise à jour du %s s'est terminée avec succès ThèmeChangement de ThèmeThèmesIl y a une %s de %s disponible.Il y a une nouvelle version disponible de %s. Ce plugin n'a pas été indiqué comme étant compatible avec votre version actuelle de WordPressCe plugin n'a pas été testé avec votre actuelle version de WordPressTimestampTitreTotalVillePériode d'essaiTypeImpossible de se connecter au système de fichiers. Merci de confirmer vos autorisations.Licences sites illimitésMises à jour illimitéesCommissions illimitées.Jusqu'à %s SitesMise à jourMettre à jour la licenceMises à jour, annonces, marketing, pas de spamMise à jourTéléverser et activer la version téléchargéeGénialUser IDUtilisateursValeurUn email de vérification vient d'être envoyé sur %s. Si vous ne le recevez pas d'ici 5 minutes, merci de vérifier dans vos spams.VérifiéVérifier l'emailLa version %s vient d'être publiée.Voir les détailsVoir les fonctionnalités payantesAttentionNous ne trouvons aucune licence active associée avec cette adresse email, êtes-vous qu'il s'agit de la bonne adresse ?Nous ne trouvons pas votre adresse mail dans notre système, êtes-vous qu'il s'agit de la bonne adresse ?Nous avons fait quelques modifications au %s, %sNous sommes impatient de vous présenter l'intégration Freemius au niveau réseau.Statistiques du site web, de l'adresse email et des réseaux sociaux (optionnel)À quoi vous attendiez-vous ?Quelle fonctionnalité ?Quel est votre %s ?Quel prix seriez-vous prêt à payer ?Que recherchez-vous ?Quel est le nom du %s ?Où allez-vous faire la promotion du %s ? Page WordPress.org du pluginOuiOui - %sVous avez déjà utilisé la période d'essai.Vous êtes à 1 clic de commencer votre période d'essai gratuite de %1$s jours de la formule %2$s.Vous êtes tout bon !Vous utilisez déjà le %s en période d'essai. Il ne reste qu'une étape - %sVous pouvez toujours profiter de toutes les fonctionnalités de %s mais vous n'aurez plus accès aux mises à jour de sécurité ou de fonctionnalités de %s, ni au support.Vous n'avez pas de licence valide pour accéder à la version premium.Vous avez une license pour %s.Votre %s a bien été mis à jour.Peut-être que cela vous a échappé mais vous n'êtes pas obligé de partager la moindre information et vous pouvez juste %s l'enregistrement.Vous avez déjà validé notre suivi d'utilisation qui nous permet de continuer à améliorer le %s.Vous avez déjà validé notre suivi d'utilisation qui nous permet de continuer à les améliorer.Votre Add-on %s a bien été mis à jour.Votre période d'essai %s a bien été annulé.Votre compte a été activé avec succès avec la formule %s.Votre dossier d'affiliation pour %s a été accepté ! Identifiez-vous dans votre espace affilié sur : %s.Votre compte affilié a été suspendu temporairement.Votre email a été vérifié avec succès - vous êtes FORMIDABLE !Votre période d'essai gratuite est terminée. %1$sFaites la mise à jour maintenant%2$s pour continuer à utiliser le %3$s sans interruption.Votre période d'essai gratuite est terminée. Vous pouvez continuer à utiliser toutes nos fonctionnalités gratuites.Votre licence a été annulé. Si vous pensez qu'il s'agit d'une erreur, merci de contacter le support.Votre licence a expiré.%1$sFaites la mise à jour maintenant%2$s pour continuer à utiliser le %3$s sans interruption.Votre licence a expiré. Vous pouvez toujours utiliser les fonctionnalités %s mais vous devrez renouveler votre licence pour recevoir les mises à jour et une assistance.Votre licence a expiré. Vous pouvez toujours utiliser la version gratuite indéfiniment.Votre licence a bien été activée.Votre licence a bien été désactivé, vous utilisez à présent la formule %s.Votre nom a été mis à jour.Votre formule a bien été modifié vers %s. Votre formule a bien été mise à jour.Votre abonnement a bien été annulé. Votre licence de la formule %s expirera dans %s.Votre période d'essai a bien démarré.Code postalDirectement%s ne peut pas fonctionner sans %s.%s ne peut pas fonctionner sans le plugin.Avertissementautoriser%s restante(s)Activation en coursannéeAPIFermerDebuggageFélicitationsBloquéConnectéTélécharger la dernière versionTélécharger la dernière version gratuiteMensuelExpirationCheminEmail en cours d'envoimoisAnnuelAnnuelUne foisFormuleClef secrète manquanteVersions du SDKLicenceSynchroniserSynchroniser la licenceAuteurOffOnBasé sur %sCommencer l'essai gratuitFermerFermerDésactivationdéléguerne %sPAS%s m'envoyer de mises à jour de sécurité ou de fonctionnalités, ni de contenu instructif, ni d'offre.Formule %s%s FacturéBestHeyOupsHey %s,YoupilicenceSitesmsNouvelle versionNon vérifiéTarifTarifsVersionsecpasserHmmcommencer la période d'essaiabonnementChangementla dernière version de %s iciessaiPériode d'essaiSupprimerRétrograderÉditerCacherInscriptionDésinscriptionAcheterAfficherPasserMise à jourMise à jourIl y a %sfreemius/languages/freemius-es_ES.mo000064400000147335147600046700013522 0ustar00:##A##n$h%Sz%%% % & &&6&&]&_'#r'' ' ' ''''''@'H<((O((( )+)3)C)K) T)`)q)))&)-)* *!*0*E*X*_*5g*** * *'** + +#+o4+++GJ,T,,---"-.2.!R.at.+.0/3/E/\/k/s/////// / // /E/;0X0_0s0 11%1 91 F1 P1^1o1Y111 2 22&262 O2(Z212%2:233,343 D3 O3\3r3 z33 33x3 4 4 44a5y555t5%6?6U6k6t6666 667[68f88 8 9'9+9Y?9a99:!: ): 7:;E::::; ; ; ;;j;0<5?< u<<3<2<<~=U=== >#>)>>-h>>S>>'?>?MA?7?g?p/@@@y@:ASA sAAAA%A AB BB1B.BO C[CACyDDDaD/EB3E,vEE E EEE EF FF %F 0F_"_B_6`=J`` ``-``a&aEa*_aaa$aMa b/bLbolb>bc 2c&ScZzcTcR*d.}d.dd-e9fZUf3f<fb!gPgUg_+hhK&i(riGi#i%j)-j$WjU|j)jjk+k;@k6|k>kkkl3l$Ilnllll&l*m70mhmmm3mmmn-nAn*^n1nnn#no+o=o MoYoyoo ooNo p)pGpbprppp1pppp q q *q 7q BqOqXq pq ~q9qCqrQ r_r or |rrrr rr r r r r s s s $s 0s =sJsW@xxwypy&pz1zz zzz9{?{k|#l|| |||| | ||}V }Zb}}W}1~5~>~G~N~d~m~ u~~~~~0~/8QZo  A ! *36jr€5>U`9%x'&݃>%:h`4ɄGF^w   х ܅-K>+ֆn} Ň χ݇z  ƈ̈$ 9;R; ʉԉ܉  #; AN T_hs܊ #`"Ɍ"Wz ۍ %yu7! X($ -:6I;˓ғ  zDFBL~u."ܖ$#$-Hv_,2R59l˜/#әٙ#])ؚC,Ly *U4O=Нeo `5qƟ#ן !*3GVh" B #'.6> Wem~ ˡѡ.%@ ISV6]>"Ӣ ;N|Rϣ S _$m#Τ7+0\DbM+ &20NhNN=/m4i'$ Ԫ  2 ? K\X٫3.DUqx Ҭ   5+;tgܭ ! 7A E R]fx 1xe&ޯ0Fd !̰ Ӱ߰M&2PY@G[#z ò ϲܲ#'=ݳ4!1ٴ n$A۵$/Tk0Z (LC6 zO U_{V׹ .9.J y' Ѻߺ} } Ļѻ"0MI.@xɾ0$?[#z)ȿ˿*ӿRQ'eqB^v$gU!Rw42U216n7`AmdHic{O%'uA(&'!NYp0 &< cm v  & 7: @KSX jx  ' g    "3K7^ #@Sov   %s to access version %s security & feature updates, and support. The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box. The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is the new owner of the account.%s minimum payout amount.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s to access version %s security & feature updates, and support.%s tracking cookie after the first visit to maximize earnings potential.%s's paid features%sClick here%s to choose the sites where you'd like to activate the license on.APIASCII arrow left icon←ASCII arrow right icon➤AccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.An unknown error has occurred while trying to set the user's beta mode.An unknown error has occurred while trying to toggle the license's white-label mode.An unknown error has occurred.An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre you sure you want to delete all Freemius data?Are you sure you want to proceed?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Associate with the license owner's account.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBetaBillingBilling & InvoicesBlockingBlog IDBodyBundle PlanBusiness nameBuy a license nowBuy licenseBy changing the user, you agree to transfer the account ownership to:Can't find your license key?CancelCancel %s & ProceedCancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site.Cancel %s?Cancel InstallationCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanChange UserCheckoutClear API CacheClear Updates TransientsClick hereClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site.Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDebug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the "Stop Debug" link.Delegate to Site AdminsDelete All AccountsDetailsDisabling white-label modeDon't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support.Don't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload Paid VersionDownload the latest %s versionDownload the latest versionDownloadedDue to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.During the update process we detected %d site(s) that are still pending license activation.During the update process we detected %s site(s) in the network that are still pending your attention.EmailEmail addressEnabling white-label modeEndEnter email addressEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used for the upgrade below and we will resend you the license key.ErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.FreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFreemius is our licensing and software updates engineFull nameFunctionGet commission for automated subscription renewals.Get updates for bleeding edge Beta versions of %s.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I Agree - Change UserI can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf you click it, this decision will be delegated to the sites administrators.If you have a moment, please let us know why you are %sIf you would like to give up the ownership of the %s's account to %s click the Change Ownership button.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.Important Upgrade Notice:In %sIn case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?Install Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid module ID.Invalid new user ID or email address.Invalid site details collection.InvoiceIs ActiveIs this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin.It looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's not what I was looking forJoin the Beta programJust letting you know that the add-ons information of %s is being pulled from an external server.KeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense IDLicense KeyLicense issues?License keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerMessageMethodMigrate Options to NetworkMobile appsModuleModule PathModule TypeMore information about %sNameNetwork BlogNetwork UserNewNew Version AvailableNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license.Opt InOpt OutOpt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.PayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please enter the license key to enable the debug mode:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.Please provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProceedProcess IDProcessingProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicensePurchase MoreQuick FeedbackQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRenew your license nowRequestsRequires WordPress VersionResultSDKSDK PathSave %sSavedScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes.Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSimulate Network UpgradeSimulate Trial PromotionSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSo you can reuse the license when the %s is no longer active.Social media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart DebugStart TrialStart my free %sStateStop DebugSubmitSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a %s of %s available.There is a new version of %s available.This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.TimestampTitleTo enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your "My Profile" section of your User Dashboard:TotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser DashboardUser IDUser keyUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We couldn't load the add-ons list. It's probably an issue on our side, please try to come back in few minutes.We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)Welcome to %s! To get started, please enter your license key:What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress.org Plugin PageWould you like to proceed with the update?YesYes - %sYou already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.You do not have a valid license to access the premium version.You have a %s license.You have purchased a %s license.You have successfully updated your %s.You might have missed it, but you don't have to share any data and can just %s the opt-in.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s.Your %s license was successfully deactivated.Your account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully activated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your subscription was successfully cancelled. Your %s plan license will expire in %s.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onactive add-onActiveaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,installed add-onInstalledinterjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew Beta versionnew versionnot verifiednounPricenounPricingoptionalproduct versionVersionrevert it nowsecondssecseems like the key you entered doesn't match our records.send me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Leo Fajardo , 2022 Language-Team: Spanish (Spain) (http://app.transifex.com/freemius/wordpress-sdk/language/es_ES/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: es_ES Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2; X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 %s para acceder a la versión %s de actualizaciones de funciones, seguridad y soporte. El %s de %s enlace de descarga de %s, la clave de licencia y las instrucciones de instalación se han enviado a %s. Si no encuentras el correo electrónico después de 5 minutos, comprueba tu bandeja de correo no deseado. La versión de pago de %1$s ya está instalada. Por favor, actívala para empezar a beneficiarte de las características de %2$s. %3$s%1$s detendrá inmediatamente todos los pagos recurrentes futuros y tu licencia del plan %2$s caducará en %3$s.Completar la activación de "%s" ahoraEl complemento %s ha sido comprado correctamente.%s Instalaciones%s Licenciashace %s%s y sus complementos%s comisión cuando un cliente compra una nueva licencia.la prueba gratuita de %s fue cancelada con éxito. Puesto que el complemento es sólo premium se desactivó automáticamente. Si quieres utilizarlo en el futuro, deberás comprar una licencia.%s es un complemento único de premium. Tienes que comprar una licencia primero antes de activar el plugin.%s es el nuevo dueño de la cuenta.%s cantidad mínima a pagar.%s o mayor%s calificación%s calificaciones%s seg%s estrella%s estrellas%s vez%s veces%s para acceder a la versión %s de actualizaciones de funciones, seguridad y soporte.%s tracking cookie después de la primera visita para maximizar las ganancias potenciales.%s características de pago%sClick aquí %s para elegir los sitios sobre los que te gustaría activar la licencia.API←➤CuentaDetalles de la cuentaAccionesActivarActivar %sActivar plan %sActivar características %sActivar versión gratuitaActivar licenciaAplicar licencia en todos los sitios pendientes.Activar licencia en todos los sitios de la red.Activar este complementoActivadoComplementos para %sComplementos del módulo %sAñadir otro dominioComplementoComplementosEl complemento debe implementarse en WordPress.org o en Freemius.DirecciónLínea de la dirección %dAfiliadoAfiliaciónDespués de su período gratuito %s, pague sólo %sDe acuerdo y activar licenciaTodas las peticionesTodos los TiposPermitir y continuarAlternativamente, puedes saltarlo ahora y activar la licencia después, en tu %s página de cuenta a nivel de red.CantidadUna descarga automatizada y la instalación de %s (versión de pago) de %s comenzará en %s. Si quieres hacerlo manualmente - haz clic en el botón de cancelación.Se ha producido un error desconocido al intentar establecer el modo beta del usuario.Se ha producido un error desconocido al intentar activar el modo de marca blanca de la licencia.Se ha producido un error desconocido.Una actualización a una versión Beta reemplazará tu versión instalada de %s con la última versión Beta - úsalo con precaución, y no en sitios de producción. Te hemos avisado.Comentarios anónimosAplicar en todos los sitios pendientes.Aplicar en todos los sitios de la red.Aceptar para hacerse afiliado¿Está seguro que desea eliminar todos los datos de Freemius?¿Estás seguro que quieres proceder?Como aplazamos 30 días para posible devoluciones, sólo pagamos comisiones que son de más de 30 días.Asocia con la cuenta del propietario de la licencia.La instalación automática sólo funciona para usuarios que aceptaron.Auto renovaciones en %sInstalación automáticaCalificación mediaIncreíbleHacerse afiliadoBetaFacturaciónFacturación y facturasBloqueandoID del blogCuerpoPlan combinadoNombre de la empresaCompra una licencia ahoraComprar licenciaAl cambiar al usuario, usted acepta transferir la propiedad de la cuenta a:¿No puedes encontrar tu clave de licencia?CancelarCancelar %s y procederCancelar %s - No necesito más actualizaciones de características y seguridad, ni soporte para %s porque no pretendo utilizar%s en este, u otro sitio.¿Cancelar %s?Cancelar instalaciónCancelar suscripciónCancelar período de pruebaCanceladoCancelando %sCancelando %s...Cancelando la suscripciónLa cancelación del período de prueba bloqueará inmediatamente el acceso a todas las funciones premium. ¿Estás seguro?Cambiar licenciaCambiar propietarioCambiar PlanCambiar usuarioPagarBorrar caché de la APIBorrar transients de actualizacionesHaz clic aquíHaz click aquí para utilizar el plugin de forma anónimaHaz clic para ver los comentarios con una valoración de %sClick para ver la captura de pantalla a tamaño completo %dProductosCódigoCompatible hastaContactoContactar soporteContáctanosColaboradoresNo se puede activar %s.PaísTipo de cronFechaDesactivarDesactivar licenciaDesactivar o desinstalar %s deshabilitará automáticamente la licencia, que podrás usar en otro sitio.Al desactivar tu licencia todas las características premium se bloquearán, pero posibilitará poder activar tu licencia en otro sitio. ¿Estás seguro que quieres continuar?DesactivaciónLog de DebugEl modo de depuración se ha activado con éxito y se desactivará automáticamente en 60 minutos. También puedes desactivarlo antes haciendo clic en el enlace "Detener depuración".Delegar a administradores del sitioBorrar todas las cuentasDetallesDesactivar el modo de marca blancaNo cancelar %s - Todavía estoy interesado en obtener actualizaciones de características y seguridad, así como poder contactar con soporte.¿No tienes una clave de licencia?Donar a este pluginBajando tu planDescargaDescargar versión %sDescargar la versión de pagoDescargar la última versión %sDescargar la última versiónDescargadoDebido al nuevo %sEU Reglamento General de Protección de Datos (RGPD)%s los requisitos de obligado cumplimiento requieren que proporciones tu consentimiento explícito, una vez más, confirmando que estás de acuerdo :-)Debido a la violación de nuestros términos de afiliados, hemos decidido bloquear temporalmente tu cuenta de afiliación. Si tienes alguna pregunta, por favor contacta nuestro soporte.Durante el proceso de actualización hemos detectado%d sitio(s) que aún están pendientes de la activación de licencia.Durante el proceso de actualización detectamos %s sitio(s) en la red que todavía están pendientes de tu atención.Correo electrónicoDirección de correo electrónicoActivar el modo de marca blancaFinIntroduce el correo electrónicoIntroduce el dominio de tu sitio web o de otros sitios web donde planeas promocionar %s.Escribe abajo la dirección de correo electrónico que has usado para la actualización y te reenviaremos la clave de licencia.ErrorError recibido del servidor:CaducadoCaduca en %sDominios extraDominios extra desde donde promocionarás el producto.ArchivoFiltroPara el cumplimiento de las directrices de WordPress.org, antes de empezar el período de prueba te pedimos que aceptes con tu usuario e información no sensible del sitio web, permitiendo a %s enviar datos periódicamente a %s para comprobar si hay actualizaciones de versión y para validar la versión de prueba.GratisPeríodo de prueba gratuitoVersión gratuitaAPI FreemiusDebug FreemiusFreemius SDK no pudo encontrar el archivo principal del plugin. Por favor contacta a sdk@freemius.com con el error actual.Estado FreemiusFreemius es nuestro motor de licencias y actualizaciones de softwareNombre completoFunciónObtén comisiones por renovaciones automatizadas de las suscripciones.Obten actualizaciones para las versiones Beta de vanguardia de %s.¿Tienes una clave de licencia?Hey, ¿sabías que %s tiene un programa de afiliados? ¡Si te gusta %s puedes convertirte en nuestro embajador y ganar dinero!¿Qué te pareció %s hasta ahora? Prueba todas nuestras funciones premium de %s con una prueba gratuita de %d-días.¿Cómo subirlo y activarlo?¿Como nos promocionarás?Estoy de acuerdo - Cambiar usuarioNo puedo pagarlo durante más tiempoNo entiendo cómo hacerlo funcionarNo me gusta compartir mi información contigoHe encontrado un %s mejorHe actualizado mi cuenta, pero cuando intento sincronizar la licencia, el plan sigue siendo %s.Ya no necesito el %sSólo necesitaba la %s por un corto períodoIDSi haces click, esta decisión será delegada a los administradores de los sitios.Si tienes un momento, por favor, dinos por qué estás %sSi deseas renunciar a la titularidad de la cuenta de %s a %s haz clic en el botón de cambio de titularidad.Si quieres utilizar %s en estos sitios, introduce por favor tu clave de licencia abajo y haz click en el botón de activación.Aviso importante de actualización:En %sEn caso de que NO estés planeando utilizar este %s en este sitio (o en cualquier otro sitio), ¿te gustaría cancelar también %s?Instalar la versión gratuita ahoraInstalar la actualización gratuita ahoraInstalar ahoraInstalar actualización ahoraInstalando plugin: %sId de módulo no válido.Nuevo ID de usuario o dirección de correo electrónico no válido.Colección de detalles del sitio no válida.FacturaEstá activo¿Es este el sitio de clientes? %s si deseas ocultar información sensible como tu correo electrónico, clave de licencia, precios, dirección de facturación y facturas de la administración de WP.Parece que la licencia no se pudo activar.Parece que la desactivación de licencia ha fallado.Parece que ya no estás en modo de prueba, así que no hay nada que cancelar :)Parece que todavía estás en el plan %s. Si actualizaste o cambiaste tu plan, probablemente sea un problema de nuestra parte - lo sentimos.Parece que tu sitio actualmente no tiene una licencia activa.Parece que uno de los parámetros de autenticación es incorrecto. Actualiza tu clave pública, clave secreta e ID de usuario e inténtelo de nuevo.No es lo que estaba buscandoÚnete al programa BetaSólo déjanos informarte que la información de complementos de %s se está extrayendo de un servidor externo.ClavePor favor, comparte lo que no funcionó para que podamos arreglarlo para los futuros usuarios...Por favor, dínos la razón para que podamos mejorar.ÚltimoÚltima actualizaciónÚltima licenciaÚltima versión gratuita instaladaÚltima versión instaladaSaber másLongitudLicenciaAcuerdo de licenciaID de licenciaClave de licencia¿Problemas de licencia?Clave de licenciaLa clave de licencia está vacía.Permanente¿Te gusta %s? Conviértete en nuestro embajador y gana dinero ;-)Cargar opción de BDLocalhostLogLoggerMensajeMétodoMigrar opciones a la redApps móvilesMóduloRuta del móduloTipo de móduloMás información sobre %sNombreBlog de redUsuario de redNuevoNueva versión disponibleVersión gratuita más reciente (%s) instaladaVersión más reciente (%s) instaladaBoletínSiguienteNoSin IDSin compromiso para %s - cancelar en cualquier momentoSin compromiso por %s días - ¡cancelar en cualquier momento!No se necesita tarjeta de créditoSin caducidadSin caducidadNinguno de los planes de %s soportan un período de prueba.O.KUna vez que caduque tu licencia todavía puedes utilizar la versión gratuita pero NO tendrás acceso a las funciones de %s.Una vez que tu licencia caduque no podrás seguir utilizando %s, a no ser que lo actives de nuevo con una licencia premium válida.InscribirseDarse de baja¡Inscríbite para hacer "%s" Mejor!OtraCorreo electrónico del propietarioID del propietarioNombre del propietarioCompatible con PCIEl complemento de pago se debe implementar en Freemius.Dirección de correo electrónico de PayPalPagosLos pagos son en USD y se procesan mensualmente por medio de PayPal.PlanEl plan %s no existe, por lo tanto, no puedes comenzar un período de prueba.El plan %s no admite un período de prueba.ID del planContacta aquí con nosotrosPor favor contáctanos con el siguiente mensaje:Por favor descarga %s.Por favor, introduce la clave de licencia que recibiste en el correo electrónico al realizar la compra:Por favor, introduce la clave de licencia para activar el modo de depuración:Siéntete libre de proporcionarnos estadísticas de tu sitio web o social media, p.ej. visitas únicas mensuales, número de suscriptores de correo electrónico, seguidores, etc. (mantendremos esta información confidencial)Por favor, sigue estos pasos para completar la actualizaciónIndica si deseas que te contactemos para actualizaciones de seguridad y nuevas funciones, contenido educativo y ofertas ocasionales:Ten en cuenta que no podremos abaratar los precios desactualizados para renovaciones/nuevas suscripciones después de una cancelación. Si eliges renovar la suscripción manualmente en el futuro, después de un aumento de precio, que generalmente ocurre una vez al año, se te cobrará el precio actualizado.Por favor, danos detalles de como pretendes promocionar %s (por favor, se lo más específico que puedas)Por favor, dinos tu nombre completo.PluginPágina web del pluginID del pluginInstalar pluginRegistro de cambiosDescripciónFAQCaracterísticas y preciosInstalaciónOtras notasValoracionesEl plugin es un "Serviceware" lo que significa que no tiene una versión de código premium.PluginsSincronizar plugins y temasPremiumLa versión Premium %s ha sido activada con éxito.Versión del complemento premium ya instalada.Versión premiumVersión premium ya activa.PrecioPolítica de privacidadProcederID del procesoProcesandoProductosSumario del programaMétodos de promociónProvinciaClave públicaComprar licenciaComprar másComentarios rápidosCuotaReenviar correo electrónico de activación¡Envíanos nuevos usuarios a nuestro %s y gana %s de comisión en cada venta satisfactoria que nos hayas referido!Renovar la licenciaRenueva tu licencia ahoraPeticionesNecesita la versión de WordPressResultadoSDKRuta del SDKGuardar %sGuardadoCrons programadosCapturas de pantallaBuscar por direcciónClave secretaPágina segura HTTPS %s, desde un dominio externoParece que estamos teniendo algún problema temporal con tu cancelación de la suscripción. Vuelve a intentarlo en unos minutos.Parece que estamos teniendo algún problema temporal con tu cancelación de prueba. Vuelve a intentarlo en unos minutos.Parece que tienes la última versión.Seleccionar paísEnviar clave de licenciaGuardar opción en BDSimular actualización de redSimular período de pruebaLicencia para un único sitioID del sitioSitio dado de alta correctamente.SitiosSaltar y %sRutaDe este modo, podrás reutilizar la licencia cuando el %s ya no esté activo.Social media (Facebook, Twitter, etc.)Disculpa las molestias y estamos aquí para ayudarte si nos das una oportunidad.Lo sentimos, no podemos completar la actualización de correo electrónico. Ya hay registrado otro usuario con esa dirección de correo electrónico.InicioIniciar DepuraciónComenzar el período de pruebaComenzar mi período gratuito de %sEstadoDetener la depuraciónEnviarEnviar y %sSuscripciónSoporteForo de soporteSincronizar datos desde el servidorTax / Núm IVATérminos de servicioGracias por aplicar a nuestro programa de asociados, infortunadamente, de momento hemos decidido rechazar tu petición. Por favor, prueba de nuevo en 30 días.Gracias por aplicar a nuestro programa de afiliados, revisaremos tu petición durante los próximos 14 días y te volveremos a contactar con información adicional.¡Muchas gracias por utilizar %s y sus complementos!¡Muchas gracias por utilizar %s!¡Muchas gracias por utilizar nuestros productos!¡Gracias!¡Gracias %s!Gracias por confirmar el cambio de propiedad. Se envió un correo electrónico a %s para su aprobación final.%s ha roto mi sitioEl %s no funcionabaEl %s no funciona como esperaba%s es genial, pero necesito una característica que no soportáisEl %s no funciona%s de repente ha dejado de funcionarEl proceso de instalación ha comenzado y puede tardar unos minutos en completarse. Por favor, espera hasta que se finalice - no actualices esta página.El paquete de plugin remoto no contiene una carpeta con el Slug deseado y el cambio de nombre no funcionó.La actualización de %s se completó con éxito.TemaCambiar temaTemasHay una %s de %s disponible.Hay una nueva versión de %s disponible.Este puglin no ha sido marcado como compatible con tu versión de WordPress.Este plugin no ha sido probado con tu versión actual de WordPress.TimestampTítuloPara entrar en el modo de depuración, introduce la clave secreta del propietario de la licencia (UserID = %d), que puedes encontrar en la sección "Mi perfil" de tu panel de control de usuario:TotalMunicipioPeríodo de prueba gratuitoTipoNo es posible conectarse al sistema de archivos. Por favor, confirma tus credenciales.Licencias ilimitadasActualizaciones IlimitadasComisiones Ilimitadas.Hasta %s sitiosActualizarActivar licenciaActualizaciones, anuncios, marketing, sin spamActualizarCargar y activar la versión descargadaW00tPanel de escritorio del usuarioID de usuarioClave de usuarioUsuariosValorEl correo de verificación se acaba de enviar a %s. Si no puedes encontrarlo después de 5 min, comprueba tu carpeta de spam.VerificadoVerificar correo electrónicoLa versión %s se ha lanzado.Ver detallesVer las funciones de pagoAtencionNo vemos ninguna licencia activa asociada a esa dirección de correo electrónico, ¿estás seguro de que es la dirección de correo electrónico correcta?No podemos encontrar tu dirección de correo electrónico en el sistema, ¿estás seguro de que es la dirección de correo electrónico correcta?No pudimos cargar la lista de complementos. Probablemente sea un problema por nuestra parte, por favor, inténtalo de nuevo en unos minutos.Hemos realizado algunas optimizaciones al %s, %sEstamos emocionados de introducir la integración de Freemius a nivel de red.Sitio web, correo electrónico y estadísticas de social media (opcional)¡Bienvenido a %s! Para empezar, introduce tu clave de licencia:¿Qué esperas?¿Qué característica?¿Cual es tú %s?¿Con qué precio te sentirías cómodo pagando?¿Que has estado buscando?¿Cuál es el nombre de %s?¿Dónde vas a promocionar %s?Página del plugin en WordPress.org¿Deseas continuar con la actualización?SiSi - %sYa utilizaste un período de prueba antes.Estás a sólo 1-click de comenzar tu %1$s días de prueba gratuita del plan %2$s.¡Está todo listo!Estás ejecutando %s en modo de prueba.Estás a sólo un paso - %sTodavía puedes disfrutar de todas las funciones de %s pero no tendrás acceso a soporte y actualizaciones de %s.No tienes una licencia válida para acceder a la versión premium.Tienes una licencia %s.Has comprado una licencia %s.Has actualizado correctamente tu %s.Es posible que te lo hayas perdido, pero no tienes que compartir ningún dato y puedes solo aceptar %s.Ya has optado por nuestro seguimiento de uso, lo que nos ayuda a seguir mejorando %s.Ya has optado por nuestro seguimiento de uso, lo que nos ayuda a seguir mejorando.Tu complemento %s del plan se actualizó con éxito.Tu prueba gratuita de %s fue cancelada con éxito.Tu licencia %s ha sido marcada como etiqueta blanca para ocultar información sensible del administrador de WP (por ejemplo, tu correo electrónico, clave de licencia, precios, dirección de facturación y facturas). Si alguna vez deseas revertirlo, puedes hacerlo fácilmente a través de tu %s. Si se trata de un error, también puedes %s.Tu licencia %s ha sido desactivada correctamente.Tu cuenta se ha activado correctamente con el plan %s.¡Tu aplicación al programa de afiliación para %s ha sido aceptada! Entra en tu área de afiliado desde: %s.Tu cuenta de afiliado ha sido suspendida temporalmente.Tu email ha sido verificado correctamente - ¡Eres IMPRESIONANTE!Tu período de prueba ha caducado. %1$sActualiza ahora %2$s para continuar usando el %3$s sin interrupciones.Tu período de prueba ha caducado. Todavía puedes seguir usando todas nuestras funciones gratuitas.Tu licencia ha sido cancelada. Si crees que es un error, ponte en contacto con el servicio de asistencia.Tu licencia ha caducado. %1$sActualiza ahora %2$s para continuar usando el %3$s sin interrupciones.Tu licencia ha caducado. Todavía puedes seguir usando todas las funciones de %s, pero tendrás que renovar tu licencia para seguir recibiendo actualizaciones y soporte.Tu licencia ha caducado. Puedes seguir usando el plan gratuito %s para siempre.Tu licencia fue activada correctamente.Tu licencia fue desactivada correctamente, has vuelto al plan %s.Tu nombre fue actualizado correctamente.Tu plan se activó con éxito.Tu plan se cambió correctamente a %s.Tu plan se actualizó con éxito.Tu suscripción ha sido cancelada correctamente. Tu %s licencia del plan caducará en %s.Tu versión de prueba se ha iniciado con éxito.Código postalBien hechoActivo%s no se puede ejecutar sin %s.%s no se puede ejecutar sin el plugin.Atenciónpermitirquedan %sActivandoañoAPIDescartarDepurandoFelicidadesBloqueadoConectadoDescargar la últimaDescargar la última versión gratuitaMensualCaducidadRutaEnviando correo electrónicomeAnualAnualmenteUna vezPlanSin clave secretaVersiones SDKLicenciaSincronizarSincronizar licenciaAutorApagadoEncendidobasado en %sComenzar el período de prueba gratuitoDescartarDescartardesactivandodelegar%sNO%s me envíes actualizaciones de seguridad y nuevas funcionalidades, contenido educativo y ofertas.Plan %sFacturado %sEl mejorHeyOopsHey %s,InstaladoVayalicenciaSitiosmsnueva versión Betanueva versiónno verificadoPrecioPrecioopcionalVersiónrevertirlo ahorasegparece que la clave que has introducido no coincide con nuestros registros.envíame actualizaciones de seguridad y nuevas funcionalidades, contenido educativo y ofertas.saltarHmmcomenzar el período de pruebasuscripcióncambiandola última versión %s aquíperíodo de pruebaPeríodo de Prueba GratuitoBorrarDegradarEditarOcultarInscribirseDarse de bajaComprarMostrarSaltarActualizarActualizarhace %sfreemius/languages/freemius-de_DE.mo000064400000207276147600046700013465 0ustar00<\*\*A]**nB+2+Z+h?,S,%, "- .-:-A-T-6-1._.F/f/#}//%/ / / /0 000#0@,0+m0H00O0ME1j112)22233333 33334&4-=4k4 4444445455 5 (5'45\5 u5 55o5 66G6T6P7o78,8"H8k8(828!8>9aE9+909::-:<:D:X:]:e:x:::: : :: :E:y;;;;; X<c<w< < < <<<Y<7=F= W= c=o=x=}== =(=1=% >:2>m> r>>>> > >>> >7>!? &?1?xD?? R@ _@i@@A*A2A5BAxAXAtAaB{BBBBBBB C CCrD[DfDGE ME[EpEEEYE_Ea\FFFFG G G !G;/GkGpGwGOvHH H H HHjHjI5yI II3I2I)J~=J:JUJMKiKKK)K-K LSLsL'LLgLMMZlM7M>Mm>NgNpOOOyOP8P XPdPwP PP%P PQQ-QDQ bQ&lQQ1)R.[RORRAZSSyS26TiTTaT UU%UB)U,lUU U UUU UUV V V &V2V BVNVdV4mVV VVVVVVVVW W(W /W ;WGWaW,fW W WWWnXrX X!XX XXX%X Y%Y+8YdY |Y Y/YYmY~9ZZZZi[q\\ \\ \ \)\.\V]fq]|]U^r^4{^^5^(^__-3_a_Uu_6_`1`~`|a[bbcc +c5c(Dc*mc"c1c+c*d&DdNkdddd.d)e9eIeieqee e eeeee ee ee ffW*f fffffffggggg .g:g Lg5Wgsglh&nhhh h hiiij(j0jLj Rj\j aj=mj&jLjfkk k kkkk kk k kk k l !l.l?ll%lm/mm)m n n\"nnnn6oKo^oC}oooopmpxqd|q-qr r"r)r'HrprMsGks,ss sttStuu4vw"w'w-wE2wxwwwwww*wx* x^7xxxxxxdx'y 0y=y Vydywyyyyy yyzizWznzH{"{B|6S|=|| ||-|*}H}&^}/}}#}*}~"~+~/I~Qy~U~D!$fM/o;> &"<IDZˁO&vT~RӃ.&.U-;ą9Z:3<ɆbPiU_pK (WG#ȉ%)$<aU)Xɋ;ދ6>Qь$ &B^&|*7΍;3Tˎߎ*1'Yu#ɏۏ .CW\ anNwƐ.?PU[1vđԑ 7 @ N9ZCؒQݒ/ ? LVp  ɓ ӓ ߓ    '4]Xvx'%  4˜nǝ'6^$z*  *1\8=Qӟ%[@ci mw L U `m3ˣ22 JTdyF  4L j x eئh>'ϧ)%'+ ?7*w2vթ-LGz"ª )8 =H `k s}  Wī|/ ɬӬ ̭  o8 ̮ٮ $ 2+?Jk7  $8 @Lhm?v ɰݰp ( 6@ .66L"Z ڴ $,!QsL hr .OTaloθ{>ڹ   'D5z%` / BO^G;OBX4нI{hž).X$s34!!kC8I YShBQYut!;A%1':T"o0"$@ _)iAHB\*D%"H2) 5tQ f<g ) %, 3 AK\l} D   ,6 >H'Y  / (B * K kv41:/N_5p|&R#  ". AEN ' OoDt)29\QE;$S])o    R&y/+ *0 F Q \ir  q,(, 5 BN ]iMm2(8PEa   5?R VJb(m~D  2; KV e s 4=2r - z "Lo*U*(Sos 3 +1SPb4 p[ ag{-8 DI \h{ .L%_)+#=Miqlh0L9EH6=Uq;&$-08CWMPE:=i(;'RzF R j*DL_HXfQ~17<iM.=#9ae2:4yoqd[i*[)%GO)&-'>O .^      %     ( 7 <  @  J V  n  x  (            ! / 6 ; Q W [ ^ !o        V : B V \ b  i s z                d L~       (DJO X b mx  %s to access version %s security & feature updates, and support. The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box. The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s"The ", e.g.: "The plugin"The %s's%1$s has been placed into safe mode because we noticed that %2$s is an exact copy of %3$s.%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s automatic security & feature updates and paid functionality will keep working without interruptions until %s (or when your license expires, whatever comes first).%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is my client's email address%s is my email address%s is the new owner of the account.%s minimum payout amount.%s opt-in was successfully completed.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s to access version %s security & feature updates, and support.%s to activate the license once you get it.%s tracking cookie after the first visit to maximize earnings potential.%s's paid features%sClick here%s to choose the sites where you'd like to activate the license on.Click here to learn more about updating PHP.A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours.A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder.APIAPI connectivity state is unknownUnknownASCII arrow left icon←ASCII arrow right icon➤AccountAccount DetailsAccount is pending activation. Please check your email and click the link to activate your account and then submit the affiliate form again.ActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn automated download and installation of %s (paid version) from %s will start in %s. If you would like to do it manually - click the cancellation button now.An unknown error has occurred while trying to set the user's beta mode.An unknown error has occurred while trying to toggle the license's white-label mode.An unknown error has occurred.An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre both %s and %s your email addresses?Are you sure you want to delete all Freemius data?Are you sure you want to proceed?Are you sure you would like to proceed with the disconnection?As we reserve 30 days for potential refunds, we only pay commissions that are older than 30 days.Associate with the license owner's account.Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBetaBillingBilling & InvoicesBlockingBlog IDBodyBundleBundle PlanBusiness nameBuy a license nowBuy licenseBy changing the user, you agree to transfer the account ownership to:By disconnecting the website, previously shared diagnostic data about %1$s will be deleted and no longer visible to %2$s.Can't find your license key?CancelCancel %s & ProceedCancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site.Cancel %s?Cancel InstallationCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanChange UserCheckoutCityClear API CacheClear Updates TransientsClick hereClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCommunicationCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeCurrent %s & SDK versions, and if active or uninstalledDateDeactivateDeactivate LicenseDeactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site.Deactivating your license will block all premium features, but will enable activating the license on another site. Are you sure you want to proceed?DeactivationDebug LogDebug mode was successfully enabled and will be automatically disabled in 60 min. You can also disable it earlier by clicking the "Stop Debug" link.Delegate to Site AdminsDelete All AccountsDetailsDiagnostic InfoDiagnostic data will no longer be sent from %s to %s.Disabling white-label modeDisconnecting the website will permanently remove %s from your User Dashboard's account.Don't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support.Don't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload Paid VersionDownload the latest %s versionDownload the latest versionDownloadedDue to the new %sEU General Data Protection Regulation (GDPR)%s compliance requirements it is required that you provide your explicit consent, again, confirming that you are onboard :-)Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.Duplicate WebsiteDuring the update process we detected %d site(s) that are still pending license activation.During the update process we detected %s site(s) in the network that are still pending your attention.EmailEmail addressEmail address updateEnabling white-label modeEndEnter email addressEnter the domain of your website or other websites from where you plan to promote the %s.Enter the email address you've used during the purchase and we will resend you the license key.Enter the email address you've used for the upgrade below and we will resend you the license key.Enter the new email addressErrorError received from the server:ExpiredExpires in %sExtensionsExtra DomainsExtra domains where you will be marketing the product from.FileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.For delivery of security & feature updates, and license management, %s needs toFreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFreemius is our licensing and software updates engineFull nameFunctionGet commission for automated subscription renewals.Get updates for bleeding edge Beta versions of %s.Have a license key?Hey there, did you know that %s has an affiliate program? If you like the %s you can become our ambassador and earn some cash!Homepage URL & title, WP & PHP versions, and site languageHow do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I Agree - Change UserI can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf this is a long term duplicate, to keep automatic updates and paid functionality after %s, please %s.If you click it, this decision will be delegated to the sites administrators.If you didn't get the email, try checking your spam folder or search for emails from %4$s.If you have a moment, please let us know why you are %sIf you skip this, that's okay! %1$s will still work just fine.If you wish to cancel your %1$s plan's subscription instead, please navigate to the %2$s and cancel it there.If you would like to give up the ownership of the %s's account to %s click the Change Ownership button.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.Important Upgrade Notice:In %sIn case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?Install Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid clone resolution action.Invalid module ID.Invalid new user ID or email address.Invalid site details collection.InvoiceIs %2$s a duplicate of %4$s?Is %2$s a new website?Is %2$s the new home of %4$s?Is ActiveIs active, deactivated, or uninstalledIs this your client's site? %s if you wish to hide sensitive info like your email, license key, prices, billing address & invoices from the WP Admin.It looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like you are still on the %s plan. If you did upgrade or change your plan, it's probably an issue on our side - sorry.It looks like your site currently doesn't have an active license.It requires license activation.It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.It's a temporary %s - I'm troubleshooting an issueIt's not what I was looking forJoin the Beta programJust letting you know that the add-ons information of %s is being pulled from an external server.Keep SharingKeep automatic updatesKeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense IDLicense KeyLicense issues?License keyLicense key is empty.LifetimeLike the %s? Become our ambassador and earn cash ;-)Load DB OptionLocalhostLogLoggerLong-Term DuplicateMessageMethodMigrateMigrate LicenseMigrate Options to NetworkMobile appsModuleModule PathModule TypeMore information about %sNameNames, slugs, versions, and if active or notNetwork BlogNetwork UserNever miss an important updateNever miss important updates, get security warnings before they become public knowledge, and receive notifications about special offers and awesome new features.NewNew Version AvailableNew WebsiteNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo - only move this site's data to %sNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOnce your license expires you can still use the Free version but you will NOT have access to the %s features.Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license.Opt InOpt OutOpt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info.Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info. This will help us make the %s more compatible with your site and better at doing what you need it to.Opt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.Part of an activation link message.Click herePart of the message telling the user what they should receive via email.a license keyPart of the message telling the user what they should receive via email.the installation instructionsPart of the message that tells the user to check their spam folder for a specific email.the product's support email addressPayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please enter the license key to enable the debug mode:Please feel free to provide any relevant website or social media statistics, e.g. monthly unique site visits, number of email subscribers, followers, etc. (we will keep this information confidential).Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.Please provide details on how you intend to promote %s (please be as specific as possible).Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPlugin is a "Serviceware" which means it does not have a premium code version.PluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProceedProcess IDProcessingProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicensePurchase MoreQuick FeedbackQuotaRe-send activation emailRefer new customers to our %s and earn %s commission on each successful sale you refer!Renew licenseRenew your license nowRequestsRequires PHP VersionRequires WordPress VersionReset Deactivation SnoozingResultSDKSDK PathSave %sSavedScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your subscription cancellation. Please try again in few minutes.Seems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySet DB OptionSharing diagnostic data with %s helps to provide functionality that's more relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the plugin should be translated and tailored to.Show error detailsSimulate Network UpgradeSimulate Trial PromotionSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSnooze & %sSo you can reuse the license when the %s is no longer active.Social media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart DebugStart TrialStart my free %sStateStay ConnectedStop DebugSubmitSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you for updating to %1$s v%2$s!Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.Thanks for upgrading.Thanks!The %1$s will be periodically sending essential license data to %2$s to check for security and feature updates, and verify the validity of your license.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe following products'The installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The products below have been placed into safe mode because we noticed that %2$s is an exact copy of %3$s:%1$sThe products below have been placed into safe mode because we noticed that %2$s is an exact copy of these sites:%3$s%1$sThe remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a %s of %s available.There is a new version of %s available.There was an unexpected API error while processing your request. Please try again in a few minutes and if it still doesn't work, contact the %s's author with the following:This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.This plugin requires a newer version of PHP.This will allow %s toTimestampTitleTo avoid breaking your website due to WordPress or PHP version incompatibilities, and recognize which languages & regions the %s should be translated and tailored to.To ensure compatibility and avoid conflicts with your installed plugins and themes.To enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your "My Profile" section of your User Dashboard:To let you manage & control where the license is activated and ensure %s security & feature updates are only delivered to websites you authorize.To provide additional functionality that's relevant to your website, avoid WordPress or PHP version incompatibilities that can break your website, and recognize which languages & regions the %s should be translated and tailored to.TotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser DashboardUser IDUser keyUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View %s StateView Basic %s InfoView Basic Profile InfoView Basic Website InfoView Diagnostic InfoView License EssentialsView Plugins & Themes ListView detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We couldn't load the add-ons list. It's probably an issue on our side, please try to come back in few minutes.We have introduced this opt-in so you never miss an important update and help us make the %s more compatible with your site and better at doing what you need it to.We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)Welcome to %s! To get started, please enter your license key:What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress & PHP versions, site language & titleWordPress.org Plugin PageWould you like to merge %s into %s?Would you like to proceed with the update?YesYes - %sYes - both addresses are mineYes - move all my data and assets from %s to %sYes, %%2$s is replacing %%4$s. I would like to migrate my %s from %%4$s to %%2$s.Yes, %2$s is a duplicate of %4$s for the purpose of testing, staging, or development.Yes, %2$s is a new and different website that is separate from %4$s.You already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.You do not have a valid license to access the premium version.You have a %s license.You have purchased a %s license.You have successfully updated your %s.You marked this website, %s, as a temporary duplicate of %s.You marked this website, %s, as a temporary duplicate of these sitesYou might have missed it, but you don't have to share any data and can just %s the opt-in.You should receive %3$s for %1$s to your mailbox at %2$s in the next 5 minutes.You should receive a confirmation email for %1$s to your mailbox at %2$s. Please make sure you click the button in that email to %3$s.You should receive a confirmation email for %s to your mailbox at %s. Please make sure you click the button in that email to %s.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s Add-on plan was successfully upgraded.Your %s free trial was successfully cancelled.Your %s license was flagged as white-labeled to hide sensitive information from the WP Admin (e.g. your email, license key, prices, billing address & invoices). If you ever wish to revert it back, you can easily do it through your %s. If this was a mistake you can also %s.Your %s license was successfully deactivated.Your WordPress user's: first & last name, and email addressYour account was successfully activated with the %s plan.Your affiliate application for %s has been accepted! Log in to your affiliate area at: %s.Your affiliation account was temporarily suspended.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully activated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your server is blocking the access to Freemius' API, which is crucial for %1$s synchronization. Please contact your host to whitelist the following domains:%2$sYour subscription was successfully cancelled. Your %s plan license will expire in %s.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onactivate a license hereactive add-onActiveaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismisscomplete the opt-indatadaysdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,hourhoursinstalled add-onInstalledinterjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew Beta versionnew versionnot verifiednounPricenounPricingoptionalproduct versionVersionproductsrevert it nowsecondssecseems like the key you entered doesn't match our records.send me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe above-mentioned sitesthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Oliver Heinrich, 2022-2024 Language-Team: German (Germany) (http://app.transifex.com/freemius/wordpress-sdk/language/de_DE/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: de_DE Plural-Forms: nplurals=2; plural=(n != 1); X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 %s, um auf die Sicherheits- und Funktionsupdates der Version %s und den Support zuzugreifen.Der %s-%sDownload-Link%s, der Lizenzschlüssel und die Installationsanleitung wurden an %s gesendet. Wenn du die E-Mail nach 5 Minuten nicht finden kannst, überprüfe bitte dein Spam-Postfach. Die kostenpflichtige Version von %1$s ist bereits installiert. Bitte aktiviere sie, um von den %2$s Funktionen zu profitieren. %3$sDie %s%1$s wurde in den abgesicherten Modus versetzt, weil wir festgestellt haben, dass %2$s eine exakte Kopie von %3$s ist.%1$s wird sofort alle zukünftigen wiederkehrenden Zahlungen stoppen und deine %2$s Planz-Lizenz wird in %3$s auslaufen.Schließe jetzt die "%s"-Aktivierung ab%s Add-on wurde erfolgreich erworben.%s Installationen%s Lizenzenvor %s%s und seine Add-ons%s automatische Sicherheits- und Funktionsupdates und kostenpflichtige Funktionen funktionieren bis %s (oder bis zum Ablauf deiner Lizenz, je nachdem, was zuerst eintritt).%s Provision, wenn ein Kunde eine neue Lizenz kauft.Die kostenlose Testversion von %s wurde erfolgreich storniert. Da das Add-on nur Premium ist, wurde es automatisch deaktiviert. Wenn du es in Zukunft nutzen möchtest, musst du eine Lizenz erwerben.%s ist ein reines Premium-Add-on. Du musst zuerst eine Lizenz erwerben, bevor du das Plugin aktivieren kannst.%s ist die E-Mail-Adresse meines Kunden%s ist meine E-Mail-Adresse%s ist der neue Besitzer des Kontos.%s Mindestauszahlungsbetrag.%s Opt-In wurde erfolgreich abgeschlossen.%s oder höher%s Bewertung%s Bewertungen%s s%s Stern%s Sterne%s mal%s mal%s, um auf die Sicherheits- und Funktionsupdates der Version %s und den Support zuzugreifen.%s, um die Lizenz zu aktivieren, sobald du sie erhalten hast.%s Tracking-Cookie nach dem ersten Besuch, um das Ertragspotenzial zu maximieren.Bezahlte Funktionen von %s%sKlicke hier%s um die Webseite auszuwählen, auf denen du die Lizenz aktivieren möchtest.Klicke hier, um mehr über die Aktualisierung von PHP zu erfahren.Eine Bestätigungs-E-Mail wurde gerade an %s gesendet. Der Besitzer der E-Mail-Adresse muss die Aktualisierung innerhalb der nächsten 4 Stunden bestätigen.Eine Bestätigungs-E-Mail wurde gerade an %s gesendet. Du musst die Aktualisierung innerhalb der nächsten 4 Stunden bestätigen. Wenn du die E-Mail nicht findest, überprüfe bitte deinen Spam-Ordner.APIUnbekannt←➤KontoKonto DetailsDie Aktivierung des Kontos steht noch aus. Bitte überprüfe deine E-Mails und klicke auf den Link, um dein Konto zu aktivieren, und sende dann das Affiliate-Formular erneut.AktionenAktivierenAktiviere %sAktiviere %s PlanAktiviere %s FunktionenKostenlose Version freischaltenLizenz freischaltenAktiviere die Lizenz auf allen ausstehenden Seiten.Aktiviere die Lizenz auf allen Seiten im Netzwerk.Aktiviere dieses Add-onAktiviertAdd-Ons für %sAdd-Ons von Modul %sWeitere Domain hinzufügenAdd-onAdd-onsDas Add-on muss auf WordPress.org oder Freemius bereitgestellt werden.AdresseAdresse Zeile %dAffiliateAffiliateNach deinem kostenlosen %s zahlst du so wenig wie %sZustimmen & Lizenz aktivierenAlle AnfragenAlle ArtenErlauben & FortfahrenAlternativ kannst du auch diesen Schritt überspringen und die Lizenz später auf der Netzwerk-Kontoseite deines %s aktivieren.BetragEin automatischer Download und die Installation von %s (kostenpflichtige Version) von %s wird in %s starten. Wenn du es manuell machen möchtest - klicke jetzt auf den Abbruch-Button.Beim Versuch, den Beta-Modus für den Benutzers einzustellen, ist ein unbekannter Fehler aufgetreten.Beim Versuch, auf den White-Label-Modus der Lizenz umzuschalten, ist ein unbekannter Fehler aufgetreten.Ein unbekannter Fehler ist aufgetreten.Ein Update auf eine Beta-Version ersetzt deine installierte Version von %s durch die neueste Beta-Version - verwende sie mit Vorsicht und nur auf Testseiten. Du wurdest gewarnt.Anonymes FeedbackAuf alle ausstehenden Webseiten anwenden.Auf alle Seiten im Netzwerk anwenden.Bewirb dich, um ein Affiliate zu werdenSind %s und %s beide deine E-Mail-Adressen?Bist du sicher, dass du alle Freemius-Daten löschen möchtest?Bist du sicher, dass du fortfahren willst?Möchtest du wirklich mit der Trennung fortfahren?Da wir 30 Tage für mögliche Rückzahlungen reservieren, zahlen wir nur Provisionen aus, die älter als 30 Tage sind.Mit dem Konto des Lizenzinhabers verknüpfen.Die automatische Installation funktioniert nur für eingeloggte Nutzer.Verlängert sich automatisch in %sAutomatische InstallationDurchschnittliche BewertungfantastischenPartner werdenBetaAbrechnungAbrechnung & RechnungenBlockierenBlog IDHauptteilPaketPaket-PlanGeschäftsnameJetzt eine Lizenz kaufenLizenz kaufenIndem du den Benutzer wechselst, stimmst du zu, den Besitz des Accounts zu übertragen:Durch das Trennen der Website werden zuvor geteilte Diagnosedaten über %1$sgelöscht und sind für %2$snicht mehr sichtbar.Du kannst deinen Lizenzschlüssel nicht finden?Abbrechen%s abbrechen & fortfahrenAbbrechen %s - Ich benötige keine Sicherheits- und Funktionsupdates mehr und auch keinen Support für %s, da ich nicht vorhabe, das %s auf dieser oder einer anderen Seite zu verwenden.%s abbrechen?Installation abbrechenAbonnement kündigenTestversion abbrechenAbgebrochenAbbruch von %sStoppe %s...Das Abonnement kündigenWenn du die Testversion abbrichst, wird der Zugang zu allen Premium-Funktionen sofort gesperrt. Bist du sicher?Lizenz ändernEigentümer wechselnPlan ändernBenutzer ändernVerkaufs-StadtAPI Cache löschenLöschen von Aktualisierungen RestenHier klickenKlicke hier, um das Plugin anonym zu nutzenKlicke, um Bewertungen zu sehen, die eine Bewertung von %s abgegeben habenKlicke, um den Screenshot in voller Größe zu sehen %dProdukteCodeKommunikationKompatibel bis zuKontaktKontaktiere SupportKontaktMitwirkendeKonnte %s nicht aktivieren.LandCron TypAktuelle %sund SDK-Versionen und falls aktiv oder deinstalliertDatumDeaktivierenLizenz deaktivierenWenn du das %s deaktivierst oder deinstallierst, wird die Lizenz hier automatisch deaktiviert und du kannst sie auf einer anderen Seite verwenden.Wenn du deine Lizenz deaktivierst, werden alle Premium-Funktionen gesperrt, aber du kannst die Lizenz auf einer anderen Seite aktivieren. Bist du sicher, dass du fortfahren möchtest?DeaktivierungDebug LogDer Debug-Modus wurde erfolgreich aktiviert und wird in 60 Minuten automatisch wieder deaktiviert. Du kannst ihn auch früher deaktivieren, indem du auf den Link "Stop Debug" klickst.An die Website Admins delegierenAlle Konten löschenDetailsDiagnoseinformationenDiagnosedaten werden nicht mehr von %s bis %sgesendet.Deaktivieren des White-Label-ModusDas Trennen der Website wird %sdauerhaft aus dem Konto Ihres Benutzer-Dashboards entfernt.%s bitte nicht stornieren - ich bin immer noch daran interessiert, Sicherheits- und Funktionsupdates zu erhalten, sowie den Support kontaktieren zu können.Du hast keinen Lizenzschlüssel?Für dieses Plugin spendenDowngrade deinen PlanDownloadDownload %s VersionBezahlte Version herunterladenLade die neueste %s Version herunterLade die neueste Version herunterHeruntergeladenAufgrund der neuen %sEU Datenschutzgrundverordnung (DSGVO)%s Compliance-Anforderungen ist es erforderlich, dass du deine ausdrückliche Zustimmung gibst und damit bestätigst, dass du an Bord bist :-)Aufgrund eines Verstoßes gegen unsere Partnerschaftsbedingungen haben wir beschlossen, dein Affiliate-Konto vorübergehend zu sperren. Wenn du Fragen hast, wende dich bitte an den Support.Duplizierte WebseiteWährend des Update-Prozesses haben wir %d Site(s) entdeckt, die noch auf eine Lizenzaktivierung warten.Während des Update-Prozesses haben wir %s Site(s) im Netzwerk entdeckt, die noch deine Aufmerksamkeit benötigen.E-MailE-Mail-AdresseE-Mail-Adresse aktualisierenAktivieren des White-Label-ModusEndeE-Mail-Adresse eingebenGib die Domain deiner Website oder anderer Webseiten an, von denen aus du planst, %s zu bewerben.Gib die E-Mail-Adresse ein, die du beim Kauf verwendet hast, und wir senden Dir den Lizenzschlüssel erneut zu.Gib unten die E-Mail-Adresse ein, die du für das Upgrade verwendet hast und wir senden dir den Lizenzschlüssel erneut zu.Gib die neue E-Mail-Adresse einFehlerFehler vom Server empfangen:AbgelaufenLäuft in %s abErweiterungenExtra DomainsZusätzliche Domains, von denen aus du das Produkt vermarkten wirst.DateiFilterUm die Richtlinien von WordPress.org einzuhalten, bitten wir dich vor dem Start der Testversion um ein Opt-In mit deinen Benutzer- und nicht sensiblen Seite-Informationen, damit %s regelmäßig Daten an %s senden kann, um nach Versions-Updates zu suchen und um deine Testversion zu validieren.Für die Bereitstellung von Sicherheits- und Funktionsupdates sowie die Lizenzverwaltung muss %sKostenlosKostenlose TestversionKostenlose VersionFreemius APIFreemius DebugDas Freemius-SDK konnte die Hauptdatei des Plugins nicht finden. Bitte kontaktiere sdk@freemius.com mit der gezeigten Fehlermeldung.Freemius StatusFreemius ist unser Dienstleister für Lizenzierung und Software-UpdatesVollständiger NamePositionErhalte Provisionen für automatisierte Abonnementverlängerungen.Erhalte Updates für aktuelle Beta-Versionen von %s.Hast du einen Lizenzschlüssel?Hallo, wusstest du, dass %s ein Partnerprogramm hat? Wenn du %s magst, kannst du unser Affiliate-Partner werden und etwas Geld verdienen!URL und Titel der Homepage, WP- und PHP-Versionen und Sprache der WebsiteWie gefällt dir %s bis jetzt? Teste alle %s Premium-Funktionen mit der kostenlosen %d-Tage-Testversion.Wie kann ich es hochladen und aktivieren?Wie wirst du uns bewerben?Ich stimme zu - ändere den BenutzerIch kann es nicht mehr bezahlenIch habe nicht gewusst wie ich es zum Laufen bringeIch möchte meine Informationen nicht mit dir teilenIch habe ein besseres %s gefundenIch habe mein Konto hochgestuft, aber wenn ich versuche, die Lizenz zu synchronisieren, bleibt der Plan %s.Ich brauche das %s nicht mehrIch habe das %s nur für einen kurzen Zeitraum benötigtIDWenn dies ein langfristiges Duplikat ist, dann musst du nach %s bitte %s.Wenn du es anklickst, wird diese Entscheidung an die Administratoren der Seite delegiert.Wenn Du die E-Mail nicht erhalten hast, überprüfe deinen Spam-Ordner oder suche nach E-Mails von %4$s.Wenn du einen Moment Zeit hast, lass uns bitte wissen, warum du %sWenn du das überspringst, ist das in Ordnung! %1$swird immer noch funktionieren.Wenn Du stattdessen das Abonnement deines %1$sPlans kündigen möchtest, navigiere bitte zu %2$sund kündige es dort.Wenn du die Eigentümerschaft des Kontos von %s an %s abgeben möchtest, klicke auf den Button Eigentümer wechseln.Wenn du das %s auf diesen Seiten nutzen möchtest, gib bitte deinen Lizenzschlüssel unten ein und klicke auf den Aktivierungsbutton.Wichtiger Hinweis zum Upgrade:In %sFür den Fall, dass du NICHT vorhast, diesen %s auf dieser Seite (oder einer anderen Seite) zu verwenden - möchtest du den %s auch löschen?Kostenlose Version jetzt installierenUpdate der kostenlosen Version jetzt installierenJetzt installierenUpdate jetzt installierenInstalliere das Plugin: %sUngültige Klon-Auflösungsaktion.Ungültige Modul-ID.Ungültige neue Benutzer-ID oder E-Mail Adresse.Ungültige Website-Detailsammlung.RechnungIst %2$s ein Duplikat von %4$s?Ist %2$s eine neue Website?Wurde %4$s auf %2$s umgezogen?Ist AktivIst aktiv, deaktiviert oder deinstalliertIst dies die Seite deines Kunden? %s wenn du sensible Informationen wie deine E-Mail, Lizenzschlüssel, Preise, Rechnungsadresse & Rechnungen vor dem WP Admin verstecken möchtest.Es sieht so aus, als ob die Lizenz nicht aktiviert werden konnte.Es sieht so aus, als wäre die Lizenzdeaktivierung fehlgeschlagen.Es sieht so aus, als wärst du nicht mehr im Testmodus, also gibt es nichts zu stornieren :)Es sieht so aus, als wärst du immer noch im %s-Plan. Wenn du ein Upgrade oder einen Planwechsel durchgeführt hast, ist das wahrscheinlich ein Problem auf unserer Seite - sorry.Es sieht so aus, als ob deine Seite derzeit keine aktive Lizenz hat.Das erfordert eine Lizenzaktivierung.Es scheint, dass einer der Authentifizierungsparameter falsch ist. Aktualisiere deinen Public Key, Secret Key & User ID und versuche es erneut.Es ist eine temporäre %s - ich behebe ein ProblemEs ist nicht das, wonach ich gesucht habeAm Beta Programm teilnehmenIch wollte dich nur darauf hinweisen, dass die Add-on-Informationen von %s von einem externen Server bezogen werden.Weiter teilenAutomatische Updates beibehaltenSchlüsselBitte teile uns mit, was nicht funktioniert hat, damit wir es für zukünftige Nutzer beheben können.Bitte nenne uns den Grund, damit wir uns verbessern können.LetzteZuletzt aktualisiertLetzte LizenzAktuellste kostenlose Version installiertAktuellste Version installiertMehr erfahrenLängeLizenzLizenzvertragLizenz IDLizenzschlüsselLizenzprobleme?LizenzschlüsselDer Lizenzschlüssel ist leer.LebenslangMagst du %s? Werde unser Botschafter/Affiliate und verdiene Geld ;-)Lade DB EinstellungLocalhostLogLoggerLangfristiges DuplikatNachrichtMethodeMigrierenLizenz migrierenMigriere die Einstellungen ins NetzwerkMobile AppsModulModul PfadModul TypMehr Informationen über %sNameNamen, Slugs, Versionen und ob aktiv oder nichtNetzwerk BlogNetzwerk BenutzerVerpasse nie wieder ein wichtiges UpdateVerpasse keine wichtigen Updates, erhalte Sicherheitswarnungen, bevor sie öffentlich bekannt werden, und erhalte Benachrichtigungen über Sonderangebote und tolle neue Funktionen.NeuNeue Version verfügbarNeue WebsiteNeuere kostenlose Version (%s) InstalliertNeuere Version (%s) InstalliertNewsletterNächsteNeinNein - verschiebe nur die Daten dieser Seite nach %sKeine IDKeine Verpflichtung für %s - jederzeit kündigenKeinerlei Verpflichtung für %s Tage - jederzeit kündbar!Keine Kreditkarte erforderlichKein AblaufdatumNicht auslaufendKeiner der Pläne von %s unterstützt eine Probezeit.OKSobald deine Lizenz abläuft, kannst du die Free Version immer noch nutzen, aber du hast KEINEN Zugriff auf die %s Features.Sobald deine Lizenz abläuft, kannst du das %s nicht mehr nutzen. Es sei denn, du aktivierst ihn erneut mit einer gültigen Premiumlizenz.Opt-InAbmeldenMelden dich an, um E-Mail-Benachrichtigungen für Sicherheits- und Funktionsupdates, Bildungsinhalte und gelegentliche Angebote zu erhalten und einige grundlegende Informationen zur WordPress-Umgebung zu teilen.Melde dich an, um E-Mail-Benachrichtigungen für Sicherheits- und Funktionsupdates, Bildungsinhalte und gelegentliche Angebote zu erhalten und einige grundlegende Informationen zur WordPress-Umgebung zu teilen. Dies wird uns helfen, die Kompatibilität mit deiner Website zu verbessern und uns auf das zu konzentrieren, was du benötigst.Mach mit, um "%s" besser zu machen!AndereBesitzer EmailBesitzer IDName des BesitzersPCI-konformeDas kostenpflichtige Add-on muss auf Freemius veröffentlicht werden.Hier klickenein Lizenzschlüsseldie Installationsanleitungdie Support-E-Mail-Adresse des ProduktsPayPal Konto E-Mail AdresseZahlungenDie Auszahlungen erfolgen in USD und werden monatlich über PayPal abgewickelt.PlanPlan %s existiert nicht, es kann keine Testversion gestartet werden.Der Plan %s unterstützt keine Probezeit.Plan IDBitte kontaktiere uns hierBitte kontaktiere uns mit der folgenden Nachricht:Bitte lade %s herunter.Bitte gib den Lizenzschlüssel ein, den du in der E-Mail direkt nach dem Kauf erhalten hast:Bitte gib den Lizenzschlüssel ein, um den Debug-Modus zu aktivieren:Du kannst uns gerne relevante Statistiken über deine Website oder soziale Medien zur Verfügung stellen, z.B. monatliche Besuche der Website, Anzahl der E-Mail-Abonnenten, Follower, etc. (wir werden diese Informationen vertraulich behandeln).Bitte folge diesen Schritten, um das Upgrade abzuschließenBitte lass uns wissen, wenn du möchtest, dass wir dich für Sicherheits- und Funktionsupdates, Bildungsinhalte und gelegentliche Angebote kontaktieren:Bitte beachte, dass wir nicht in der Lage sind, veraltete Preise für Verlängerungen/neue Abonnements nach einer Kündigung beizubehalten. Wenn du dich dafür entscheidest, das Abonnement in Zukunft nach einer Preiserhöhung, die in der Regel einmal im Jahr stattfindet, manuell zu verlängern, wird dir der aktualisierte Preis berechnet.Bitte gib Details an, wie du beabsichtigst, %s zu fördern (bitte sei so genau wie möglich).Bitte gib deinen vollständigen Namen an.PluginPlugin-HomepagePlugin-IDPlugin installierenChangelogBeschreibungFAQFunktionen & PreiseInstallationWeitere NotizenBewertungenDas Plugin ist eine "Serviceware", was bedeutet, dass es keine Premiumversion hat.PluginsPlugins & Themes SyncPremiumPremium %s Version wurde erfolgreich aktiviert.Premium Add-on Version bereits installiert.Premium VersionPremium Version bereits aktiv.PreisDatenschutzrichtlinieFortfahrenProzess-IDVerarbeitungProdukteProgramm ZusammenfassungPromotion MethodenProvinzÖffentlicher SchlüsselLizenz kaufenMehr kaufenSchnelles FeedbackKontingentAktivierungsmail erneut sendenEmpfehle neue Kunden an unsere %s und verdiene %s Provision für jeden erfolgreichen Verkauf, den du vermittelst!Lizenz erneuernVerlängere deine Lizenz jetztAnfragenBenötigt PHP VersionBenötigt WordPress VersionZurücksetzen des Deaktivierungs-SchlummernsErgebnisSDKSDK Pfad%s speichernGespeichertGeplante CronsScreenshotsSuche über die AdresseGeheimer SchlüsselSichere, verschlüsselte %s Seite, die von einer externen Domain geladen wirdEs scheint, als ob wir ein temporäres Problem mit der Kündigung deines Abonnements haben. Bitte versuche es in ein paar Minuten erneut.Es scheint, als hätten wir ein temporäres Problem mit der Abmeldung deiner Testversion. Bitte versuche es in ein paar Minuten erneut.Sieht so aus, als hättest du die neueste Version.Land auswählenLizenzschlüssel sendenDB Option setzenDas Teilen von Diagnosedaten mit %s hilft dabei, Funktionen bereitzustellen, die für deine Website relevanter sind, Inkompatibilitäten mit WordPress- oder PHP-Versionen zu vermeiden, die deine Website beschädigen können, und zu erkennen, auf welche Sprachen und Regionen das Plugin übersetzt und angepasst werden sollte.Fehlerdetails anzeigenNetzwerk Upgrade simulierenTrial Promotion simulierenEinzelplatzlizenzWebseiten-IDWebseite erfolgreich eingeloggt.WebseitenÜberspringen & %sURLSnooze & %sSo kannst du die Lizenz wiederverwenden, wenn die %s nicht mehr aktiv ist.Soziale Medien (Facebook, Twitter, etc.)Wir entschuldigen uns für die Unannehmlichkeiten und sind hier, um zu helfen, wenn du uns eine Chance gibst.Sorry, wir konnten das E-Mail-Update nicht abschließen. Ein anderer Benutzer mit der gleichen E-Mail ist bereits registriert.StartDebugging startenStarte TestversionStarte mein kostenloses %sStaatBleib in KontaktDebugging stoppenAbsendenAbschicken & %sAbonnementUnterstützungSupport ForumDaten vom Server synchronisierenSteuer-/Umsatzsteuer-IDAllgemeine GeschäftsbedingungenVielen Dank, dass du dich für unser Partnerprogramm beworben hast. Leider haben wir uns an dieser Stelle entschieden, deine Bewerbung abzulehnen. Bitte versuche es in 30 Tagen erneut.Vielen Dank, dass du dich für unser Partnerprogramm beworben hast. Wir werden deine Angaben in den nächsten 14 Tagen überprüfen und uns mit weiteren Informationen bei dir melden.Vielen Dank für die Aktualisierung auf %1$s v %2$s!Vielen Dank, dass du %s und seine Add-ons benutzt!Vielen Dank, dass du %s benutzt!Vielen Dank, dass du unsere Produkte benutzt!Danke dir!Danke %s!Danke für die Bestätigung des Eigentümerwechsels. Eine E-Mail wurde soeben an %s zur endgültigen Genehmigung gesendet.Danke für das Upgrade.Danke!%1$swird regelmäßig wichtige Lizenzdaten an %2$ssenden, um nach Sicherheits- und Funktionsaktualisierungen zu suchen und die Gültigkeit Ihrer Lizenz zu überprüfen.Das %s hat meine Seite beschädigtDas %s hat nicht funktioniertDas %s hat nicht wie erwartet funktioniertDas %s ist toll, aber ich brauche ein bestimmtes Feature, das nicht unterstützt wirdDas %s funktioniert nichtDas %s funktionierte plötzlich nicht mehrDie folgenden Produkte ihreDer Installationsprozess hat begonnen und kann ein paar Minuten dauern. Bitte warte, bis er abgeschlossen ist - lade diese Seite nicht neu.Die folgenden Produkte wurden in den abgesicherten Modus versetzt, weil wir festgestellt haben, dass %2$s eine exakte Kopie von %3$s:%1$s istDie folgenden Produkte wurden in den abgesicherten Modus versetzt, weil wir festgestellt haben, dass %2$s eine exakte Kopie dieser Seiten ist:%3$s%1$sDas Remote-Plugin-Paket enthält keinen Ordner mit dem gewünschten Slug und das Umbenennen hat nicht funktioniert.Das Upgrade von %s wurde erfolgreich abgeschlossen.ThemeTheme wechselnThemesEs ist ein %s von %s verfügbar.Es ist eine neue Version von %s verfügbar.Beim Verarbeiten deiner Anfrage ist ein unerwarteter API-Fehler aufgetreten. Bitte versuche es in ein paar Minuten erneut und wenn es immer noch nicht funktioniert, kontaktiere den Autor von %s mit den folgenden Angaben:Dieses Plugin wurde als nicht kompatibel mit deiner Version von WordPress markiert.Dieses Plugin wurde nicht mit deiner derzeitigen Version von WordPress getestet.Dieses Plugin erfordert eine neuere Version von PHP.Dies wird es %s ermöglichenZeitstempelTitelUm zu vermeiden, dass deine Website aufgrund von WordPress- oder PHP-Versionsinkompatibilitäten beschädigt wird, und um zu erkennen, welche Sprachen und Regionen von %sübersetzt und angepasst werden sollten.Um die Kompatibilität zu gewährleisten und Konflikte mit deinen installierten Plugins und Themes zu vermeiden.Um in den Debug-Modus zu gelangen, gib bitte den geheimen Schlüssel des Lizenzinhabers (UserID = %d) ein, den du in deinem "Mein Profil"-Bereich deines "Benutzer Dashboards" findest:Damit du verwalten und kontrollieren kannst, wo die Lizenz aktiviert wird, und um sicherzustellen, dass %s Sicherheits- und Funktionsupdates nur an von dir autorisierte Websites geliefert werden.Um zusätzliche Funktionen bereitzustellen, die für deine Website relevant sind, Inkompatibilitäten von WordPress- oder PHP-Versionen, die deine Website beschädigen können, und um zu erkennen, welche Sprachen und Regionen für %sübersetzt und angepasst werden sollten.TotalStadtTestversionTypEs kann keine Verbindung zum Dateisystem hergestellt werden. Bitte bestätige deine Anmeldedaten.Unbegrenzt LizenzenUnbegrenzte UpdatesUnbegrenzte Provisionen.Bis zu %s SeitenUpdateLizenz aktualisierenUpdates, Ankündigungen, Marketing, kein SpamUpgradeLade die heruntergeladene Version hoch und aktiviere sieW00tBenutzer DashboardBenutzer-IDBenutzerschlüsselBenutzerWertVerifizierungsmail wurde gerade an %s gesendet. Wenn du sie nach 5 Minuten nicht finden kannst, überprüfe bitte deine Spam-Ordner.VerifiziertE-Mail verifizierenVersion %s wurde freigegeben.%s Status anzeigenGrundlegende %sInformationen anzeigenGrundlegende Profilinformationen anzeigenGrundlegende Website-Informationen anzeigenDiagnoseinformationen anzeigenGrundlegende Lizenzen anzeigenListe der Plugins & Themes anzeigenDetails ansehenBezahlte Funktionen ansehenWarnungWir können keine aktiven Lizenzen sehen, die mit dieser E-Mail-Adresse verbunden sind. Bist du sicher, dass es die richtige Adresse ist?Wir konnten deine E-Mail-Adresse nicht im System finden. Bist du sicher, dass dies die richtige Adresse ist?Wir konnten die Liste der Add-ons nicht laden. Es ist wahrscheinlich ein Problem auf unserer Seite, bitte versuche es in ein paar Minuten wieder.Wir haben dieses Opt-in eingeführt, damit du kein wichtiges Update verpasst und uns hilfst, die Kompatibilität von %smit deiner Website zu verbessern und uns auf das zu konzentrieren, was du benötigst.Wir haben ein paar Anpassungen am %s gemacht. %sWir freuen uns, dir die Freemius Netzwerk-Integration vorstellen zu können.Website-, E-Mail- und Social Media-Statistiken (optional)Willkommen bei %s! Um loszulegen, gib bitte deinen Lizenzschlüssel ein:Was hast du erwartet?Welche Funktion?Wie lautet dein(e) %s?Welchen Preis würdest du als gerechtfertigt erachten?Wonach hast du gesucht?Wie lautet der Name des %s?Wo wirst du %s bewerben?WordPress- und PHP-Versionen, Sprache und Titel der WebsiteWordPress.org-Plugin-SeiteMöchtest du %s in %s zusammenführen?Willst du mit dem Update fortfahren?JaJa - %sJa - beide Adressen sind meineJa - verschiebe alle meine Daten und Vermögenswerte von %s nach %sJa, %%2$s ersetzt %%4$s. Ich möchte meine %s von %%4$s nach %%2$s migrieren.Ja, %2$s ist ein Duplikat von %4$s für Test-, Staging- oder Entwicklungszwecke.Ja, %2$s ist eine neue und andere Website, die von %4$s getrennt ist.Du hast schon einmal einen Testzeitraum in Anspruch genommen.Du bist einen Klick davon entfernt, deinen kostenlosen %1$s-Tage Testzeitraum des %2$s-Planes zu starten.Ihr seid alle gut!Du testest %s bereits.Du bist nur einen Schritt entfernt - %sDu kannst immer noch alle %s-Funktionen nutzen, aber du wirst keinen Zugang zu %s-Sicherheits- und Funktionsupdates und keinen Support bekommen.Du hast keine gültige Lizenz, um auf die Premium-Version zuzugreifen.Du hast eine %s Lizenz.Du hast eine %s-Lizenz erworben.Du hast deine %s erfolgreich aktualisiert.Du hast diese Website, %s, als temporäres Duplikat von %s markiert.Du hast diese Website, %s, als temporäres Duplikat dieser Websites markiertDu hast es vielleicht übersehen: du musst keine Daten teilen und kannst einfach das Opt-in %s.Du solltest in den nächsten 5 Minuten %1$s für %2$s an deine Mailbox um %3$s erhalten.Du solltest eine Bestätigungs-E-Mail für %1$s an dein Postfach unter %2$s erhalten. Bitte stelle sicher, dass du auf die Schaltfläche in dieser E-Mail an %3$s klickst.Du solltest eine Bestätigungs-E-Mail für %s an deine Mailbox unter %s erhalten. Bitte stelle sicher, dass du auf die Schaltfläche in dieser E-Mail an %s klickst.Du hast dich bereits für unser Nutzungs-Tracking entschieden, was uns hilft, %s weiter zu verbessern.Du hast Dich bereits für unser Nutzungs-Tracking entschieden, was uns hilft, die Benutzerfreundlichkeit weiter zu verbessern.Dein %s Add-on Plan wurde erfolgreich upgegradet.Deine %s kostenlose Testversion wurde erfolgreich storniert.Deine %s-Lizenz wurde als White-Label gekennzeichnet, um sensible Informationen vor dem WP Admin zu verbergen (z.B. deine E-Mail, Lizenzschlüssel, Preise, Rechnungsadresse und Rechnungen). Solltest du das wieder rückgängig machen wollen, kannst du das ganz einfach über dein %s tun. Wenn dies ein Fehler war, kannst du es auch %sDeine %s-Lizenz wurde erfolgreich deaktiviert.Dein WordPress-Benutzer: Vor- und Nachname und E-Mail-AdresseDein Konto wurde erfolgreich mit der %s-Lizenz aktiviert.Dein Affiliate-Antrag für %s wurde angenommen! Logge dich in deinen Affiliate-Bereich ein unter: %s.Dein Mitgliedskonto wurde vorübergehend gesperrt.Deine E-Mail wurde erfolgreich verifiziert - Glückwunsch!Deine kostenlose Testversion ist abgelaufen. %1$sUpgrade jetzt%2$s, um das %3$s weiterhin ohne Unterbrechungen zu nutzen.Dein kostenloser Testzeitraum ist abgelaufen. Du kannst aber weiterhin alle unsere kostenlosen Funktionen nutzen.Deine Lizenz wurde gelöscht. Wenn du denkst, dass es ein Fehler ist, kontaktiere bitte den Support.Deine Lizenz ist abgelaufen. %1$sUpgrade jetzt%2$s, um das %3$s weiterhin ohne Unterbrechungen zu nutzen.Deine Lizenz ist abgelaufen. Du kannst weiterhin alle %s Funktionen nutzen, aber du musst deine Lizenz erneuern, um weiterhin Updates und Support zu erhalten.Deine Lizenz ist abgelaufen. Du kannst das kostenlose %s trotzdem für immer weiter nutzen.Deine Lizenz wurde erfolgreich aktiviert.Deine Lizenz wurde erfolgreich deaktiviert, du bist zurück im %s Plan.Dein Name wurde erfolgreich aktualisiert.Dein Plan wurde erfolgreich aktiviert.Dein Plan wurde erfolgreich auf %s geändert.Dein Plan wurde erfolgreich upgegradet.Dein Server blockiert den Zugriff auf die API von Freemius, was für die Synchronisation von %1$s entscheidend ist. Bitte wende dich an deinen Host, um die folgenden Domains auf die Whitelist zu setzen: %2$sDein Abonnement wurde erfolgreich gekündigt. Dein %s Plan wird in %s ablaufen.Dein Testzeitraum wurde erfolgreich gestartet.PLZ / PostleitzahlDirekt anLizenz hier aktivierenAktiv%s kann nicht ohne %s laufen.%s kann ohne das Plugin nicht laufen.Kopf hocherlauben%s übrigAktivieren vonJahrAPIVerwerfenFehlersucheHerzlichen GlückwunschBlockiertVerbundenNeuester DownloadNeueste kostenlose Version herunterladenMonatlichVerfallsPfadE-Mail sendenmtl.JährlichJährlichEinmalPlanKein geheimer SchlüsselSDK VersionenLizenzSyncSynchronisiere LizenzAutorAusAnbasierend auf %sStarte die kostenlose TestversionSchließenSchließenschließe die Anmeldung abDatenTagedeaktivieredelegiereschicke mir %sKEINE%s Sicherheits- und Funktionsupdates, Bildungsinhalte und Angebote.%s PlanAbgerechnet wird %sBesteHalloHopplaHallo %s,StundeStundenInstalliertHurraLizenzWebseitenmsneue Beta Versionneue Versionnicht geprüftPreisPreisoptionalVersionProduktejetzt rückgängig machen.ses scheint, dass der von dir eingegebene Schlüssel nicht mit unseren Aufzeichnungen übereinstimmt.schicke mir Sicherheits- und Funktionsupdates, Bildungsinhalte und Angebote.überspringenHmmdie Testversion startenAbonnementveränderedie oben erwähnten Websitesdie neueste %s Version hiertesteTestLöschenDowngradeBearbeitenVersteckenAnmeldenAbmeldenKaufenAnzeigenÜberspringenUpdateUpgradevor %sfreemius/languages/freemius-da_DK.mo000064400000076115147600046700013463 0ustar00lS% 7 COV#i    . 6 F N W c t   & - ! !$!3!H![!b!j!r! ! !'!! ! !!""'":""V"y"2"!"0"#.#E#T#\#p#u#}### ## #### $$$$ 8$ E$ O$]$n$$$ $ $$$$ $($%%::%u%z%%% % %%% %% %% & &&5&I&Q&k&&&&&&& ''"' ('6'a:'''' ' ';'"('(.( 3( >( K(X(g( v(((U(()())C)-m))S)*'*C*7F*~**** *** ++'+ >+1H+.z+O+A+;,[,q,Bu,,,, , ,-"- ;-F-M-U- g- r- ~---- ------- -. . ..9. >. K.X.\. r.!~.. ....%.+.(/ @/ N//[////// // / // 040I05N0(000-00U11d1~1242;2 K2U2(d2*2"212+ 3*93&d3333.3)3 44:4B4Q4 Y4 d4o4x444 44 4444 4 5 5)5D5K5O5X5`5f5 v55 5&555 55 6!6)6E6 K6U6 Z6&f6L6f6A7 G7 S7_7p7 v77 7 77 77 77/78);8 e8 p8\{88889C'9k99-99 99'9M:G_: ::::::::: ;;;*.;Y;*a;^;;;< <<< <-< F<S<f<in<W<"0=6S== ==-== >& >G>*a>>>$>M> ?/?N?>n?? ?&?Z @Tg@R@.A9>A<xAPAUB_\BBKWC(CGC#D%8D)^D$D)DDDEE;3E6oE>EEEF&F$_``6`W=`6````"`a 3a=aEa La Ya ca pa}aaa aaaaaa a aa a abb $b2bBbEb ]b%ibb bbbb.b4b1c Oc ]c1kcccc!cccc cdd 0d:;dvd@{d,ddd)e*e>@e;ee Afbfif xff f fff f f ff#fg'!g,Igvggggg gg ggg hh "h .h9hIhOh lhyhhhhhhhhhhh i0i MiXiji~ii ii iii i'iP'jhxjjjj k"k'k8k =k GkRk Zkgk kk8kk)k l(ld0lllllLl.m#=m"amm mm$mUmKn anmnsnyn |nnnnnnnn2o4o)=ogo lo xo ooo ooo oootp_yp+p>qDqXqiq,|qqqqq# r-r0r(8rOar r/rr> sLsas"sYsSsTQt5t't8uT=u_ueuXvXvHw9hwww"wx x>xOxVxlxrxxxx x xxxx xxx xyy 3y>yGy KyYy\ydy lyzyy yy yy yyyyyz zz z zQ)z{z zzzzzzz zzz zzz zz{ {{{ ${.{L2{ {{{ {{{ { {{ {|||||#| '|3|;|D|%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s is the new owner of the account.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s's paid featuresAPIASCII arrow left icon←ASCII arrow right icon➤AccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate license on all pending sites.Activate license on all sites in the network.Activate this add-onActivatedAdd Ons for %sAdd Ons of module %sAdd another domainAdd-OnAdd-OnsAddressAddress Line %dAffiliateAffiliationAfter your free %s, pay as little as %sAgree & Activate LicenseAll RequestsAll TypesAllow & ContinueAmountAn unknown error has occurred.Anonymous feedbackApply on all pending sites.Apply on all sites in the network.Apply to become an affiliateAre you sure you want to delete all Freemius data?Are you sure you want to proceed?Auto installation only works for opted-in users.Auto renews in %sAutomatic InstallationAverage RatingAwesomeBecome an affiliateBetaBillingBilling & InvoicesBlockingBlog IDBusiness nameBuy a license nowBuy licenseCan't find your license key?CancelCancel %s & ProceedCancel %s?Cancel InstallationCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionChange LicenseChange OwnershipChange PlanChange UserCheckoutCityClear API CacheClick hereClick here to use the plugin anonymouslyClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryCron TypeDateDeactivateDeactivate LicenseDeactivationDebug LogDelegate to Site AdminsDelete All AccountsDetailsDon't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload Paid VersionDownload the latest %s versionDownload the latest versionDownloadedDuplicate WebsiteEmailEmail addressEndEnter the email address you've used for the upgrade below and we will resend you the license key.ErrorError received from the server:ExpiredExpires in %sExtra DomainsExtra domains where you will be marketing the product from.FileFilterFreeFree TrialFree versionFreemius APIFreemius DebugFreemius StateFull nameFunctionHave a license key?How do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI have upgraded my account but when I try to Sync the License, the plan remains %s.I no longer need the %sI only needed the %s for a short periodIDIf you have a moment, please let us know why you are %sImportant Upgrade Notice:In %sInstall Free Version NowInstall Free Version Update NowInstall NowInstall Update NowInstalling plugin: %sInvalid module ID.InvoiceIs %2$s a new website?Is ActiveIt looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It looks like your site currently doesn't have an active license.It's not what I was looking forJoin the Beta programKeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense IDLicense KeyLicense keyLicense key is empty.LifetimeLoad DB OptionLocalhostLogLoggerMessageMethodMigrateMigrate LicenseMobile appsModuleModule PathModule TypeMore information about %sNameNetwork BlogNetwork UserNewNew Version AvailableNew WebsiteNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo IDNo commitment for %s - cancel anytimeNo commitment for %s days - cancel anytime!No credit card requiredNo expirationNon-expiringNone of the %s's plans supports a trial period.O.KOpt InOpt OutOpt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleOther NotesPlugin installer section titleReviewsPluginsPlugins & Themes SyncPremiumPremium %s version was successfully activated.Premium add-on version already installed.Premium versionPremium version already active.PricingPrivacy PolicyProceedProcess IDProcessingProductsProgram SummaryPromotion methodsProvincePublic KeyPurchase LicensePurchase MoreQuick FeedbackQuotaRe-send activation emailRenew licenseRenew your license nowRequestsRequires WordPress VersionResultSDKSDK PathSave %sSavedScheduled CronsScreenshotsSearch by addressSecret KeySeems like you got the latest release.Select CountrySend License KeySet DB OptionSimulate Network UpgradeSingle Site LicenseSite IDSite successfully opted in.SitesSkip & %sSlugSnooze & %sSocial media (Facebook, Twitter, etc.)Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart DebugStart TrialStart my free %sStateStop DebugSubmitSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.Thanks!The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a new version of %s available.This plugin has not been marked as compatible with your version of WordPress.This plugin has not been tested with your current version of WordPress.TimestampTitleTotalTownTrialTypeUnlimited LicensesUnlimited UpdatesUnlimited commissions.Up to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser DashboardUser IDUser keyUsersValueVerifiedVerify EmailVersion %s was released.View detailsView paid featuresWarningWe can't see any active licenses associated with that email address, are you sure it's the right address?We couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWebsite, email, and social media statistics (optional)What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?Where are you going to promote the %s?WordPress.org Plugin PageWould you like to proceed with the update?YesYes - %sYou already utilized a trial before.You are 1-click away from starting your %1$s-day free trial of the %2$s plan.You are all good!You are already running the %s in a trial mode.You are just one step away - %sYou do not have a valid license to access the premium version.You have a %s license.You have purchased a %s license.You have successfully updated your %s.You might have missed it, but you don't have to share any data and can just %s the opt-in.You've already opted-in to our usage-tracking, which helps us keep improving the %s.You've already opted-in to our usage-tracking, which helps us keep improving them.Your %s free trial was successfully cancelled.Your account was successfully activated with the %s plan.Your email has been successfully verified - you are AWESOME!Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully activated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your trial has been successfully started.ZIP / Postal Codea positive responseRight onactivate a license hereactive add-onActiveaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.advance notice of something that will need attention.Heads upallowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %scall to actionStart free trialclose a windowDismissclose windowDismissdaysdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,hourhoursinstalled add-onInstalledinterjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew Beta versionnew versionnot verifiednounPricenounPricingoptionalproduct versionVersionproductssecondssecsend me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Joachim Jensen, 2019-2020,2022-2023 Language-Team: Danish (Denmark) (http://app.transifex.com/freemius/wordpress-sdk/language/da_DK/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: da_DK Plural-Forms: nplurals=2; plural=(n != 1); X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 Færdiggør aktivering af "%s" nuBetalingen for tilføjelsen %s blev gennemført.%s installeringer1%s licenser%s siden%s og tilføjelser%s er den nye ejer af kontoen.%s eller højere%s vurdering%s vurderinger1%s sek%s stjerne%s stjerner%s gang%s gange%s's betalte featuresAPI←➤KontoKontodetaljerHandlingerAktiverAktiver %sAktiver %s planAktiver funktioner i %sAktiver gratis versionAktiver licensAkiver licens på alle afventende websteder.Aktiver licens på alle websteder i netværket.Aktiver denne tilføjelseAktiveretTilføjelser til %sTilføjelser til modul %sTilføj andet domæneTilføjelseTilføjelserAdresseAdresselinje %dAffiliateAffiliationEfter dine gratis %s er prisen kun %sAccepter & aktiver licensAlle forespørgslerAlle typerTillad & FortsætBeløbDer skete en ukendt fejl.Anonym feedbackAnvend på alle afventende websteder.Anvend på alle websteder i netværket.Ansøg om at blive en affiliateEr du sikker på, du vil slette al Freemius data?Er du sikker på, du vil fortsætte?Auto-installation fungerer kun for tilmeldte brugere.Auto-fornyer om %sAutomatisk installeringGennemsnitlig vurderingSejtBliv en affiliateBetaBetalingFaktureringBlokererBlog-IDFirmanavnKøb en licens nuKøb licensKan du ikke finde din licensnøgle?AnnullerAnnuller %s og fortsætAnnuller %s?Annuller installeringAnnuller abonnementAnnuller prøveperiodeAnnulleretAnnullerer %sAnnullerer %s...Annullerer abonnementetSkift licensSkift ejerskabSkift planSkift brugerUdtjekningByRyd API-cacheKlik herKlik her for at benytte pluginnet anonymtKlik for at vise skærmbillede %d i fuld skærmProdukterKodeKompatibel op tilKontaktKontakt supportKontakt osBidragsydereKunne ikke aktivere %s.LandCron TypeDatoDeaktiverDeaktiver licensDeaktiveringFejlfindingslogUddeleger til webstedsadministratorerSlet alle kontiDetaljerHar du ikke en licensnøgle?Donér til dette pluginNedgraderer din planDownloadDownload 1%s versionHent betalt versionDownload den seneste version af %sDownload den seneste versionDownloadetKopier webstedE-mailE-mailadresseSlutIndtast e-mailadressen, som du benyttede ved opgraderingen, nedenfor og vi vil gensende licensnøglen til dig.FejlFejl modtager fra serveren:UdløbetUdløber om %sEkstra domænerAndre domæner du vil markedsføre produktet fra.FilFilterGratisGratis prøveperiodeGratis versionFreemius APIFreemius DebugFreemius tilstandFulde navnFunktionHar du en licensnøgle?Hvad synes du om %s indtil videre? Test alle vores premium funktioner i %s med en %d-dags gratis prøveperiode.Upload og aktivering, hvordan?Hvordan vil du promovere os?Jeg kan ikke længere betale for detJeg forstod ikke, hvordan jeg skulle få det til at fungere.Jeg har ikke lyst til at dele mine informationer med jerJeg fandt et bedre %sJeg har opgraderet min konto, men når jeg forsøger at synkronisere licensen, forbliver planen %s.Jeg har ikke længere brug for %sJeg behøvede kun %s i en kort periodeIDHvis du har tid, så lad os venligst vide hvorfor du %sVigtig meddelelse til opgradering:Om %sInstaller gratis version nuInstaller opdatering til gratis version nuInstaller nuInstaller opdatering nuInstallerer plugin: %sUgyldigt modul-ID.FakturaEr %2$s et nyt websted?Er aktivDet ser ud til, at licensen ikke kunne aktiveres.Det ser ud til, at licens-deaktiveringen mislykkedes.Det lader ikke til du er i en prøveperiode længere, så der er ikke noget at annullere :-)Det ser ud til, at dit websted endnu ikke har en aktiv licens.Det er ikke, hvad jeg søgteDeltag i beta-programmetNøgleVær venlig at dele hvad der ikke virkede så vi kan rette det for kommende brugere....Fortæl os venligst årsagen, så vi kan forbedre det.SidsteSenest opdateretSeneste licenseSeneste gratis version installeretSeneste version installeretLæs mereLængdeLicensLicensaftaleLicens-IDLicensnøgleLicensnøgleLicensnøglen er tom.LivstidHent DB-indstillingLocalhostLogLoggerBeskedMetodeFlytFlyt licensMobil-appsModulModul-stiModultypeMere information om %sNavnNetværksblogNetværksbrugerNyNy version tilgængeligNyt webstedNyere gratis version (%s) installeretNyere version (%s) installeretNyhedsbrevNæsteNejIntet IDIngen bindinger i %s - annuller når som helstIngen bindinger i %s dage - annuller når som helst!Betalingskort ikke påkrævetUdløber ikkeUdløber ikkeIngen af %s's planer understøtter prøveperiode.O.KTilmeldFrameldAccepter for at gøre "%s" bedre!AndetE-mailadresse for ejerEjer-IDEjer-navnPCI-kompatibelE-mailadresse til PayPal-kontoBetalingerUdbetalinger er i USD og behandles hver måned via PayPal.PlanPlan %s eksisterer ikke og kan derfor ikke starte prøveperiode.Plan %s understøtter ikke en prøveperiode.Plan-IDKontakt os herKontakt os venligst med følgende besked:Download venligst %s.Indtast licensnøglen, du modtog i e-mailen lige efter købet:Følg venligst disse trin for at færdiggøre opgraderingenLad os vide, om vi har lov til at kontakte dig med sikkerheds- og feature-opdateringer, informativt indhold og lejlighedsvise tilbud:Indtast venligst dit fulde navn.PluginPlugin-webstedPlugin-IDPlugin-installeringÆndringslogBeskrivelseFAQFunktioner og priserInstalleringAndre noterAnmeldelserPluginsSynkronisering af plugins og temaerPremiumPremium-versionen af %s blev aktiveret.Premium tilføjelse er allerede installeret.Premium versionPremium version allerede aktiv.PriserPrivatlivspolitikFortsætProces-IDArbejderProdukterProgramoversigtPromoveringsmetoderProvinsOffentlig nøgleKøb licensKøb flereHurtig feedbackKvoteGensend e-mail om aktiveringForny licensForny din licens nuForespørgslerKræver WordPress-versionResultatSDKSDK-stiSpar %sGemtPlanlagte cron jobsSkærmbillederSøg efter adressePrivat nøgleDet ser ud til, at du har den seneste udgivelse.Vælg landSend licensnøgleSæt DB-indstillingSimuler netværksopgraderingEnkelt site licensWebsteds-IDWebsted er tilmeldt.WebstederSpring over & %sKortnavnUdsæt & %sSociale medier (Facebook, Twitter osv.)Vi beklager ulejligheden, og vi er her for at hjælpe, hvis du giver os chancen.Beklager, vi kunne ikke opdatere e-mailen. Der er allerede registreret en anden bruger med samme e-mail.StartStart fejlfindingStart prøveperiodeStart mine gratis %sStatStop fejlfindingSendSend & %sAbonnementSupportSupportforumSynkroniser data fra serverMoms / VAT IDServicevilkårMange tak for, at du benytter %s og tilhørende add-ons!Tak fordi du benytter %s!Mange tak for at benytte vores produkter!Mange tak!Tak %s!Tak fordi du bekræftede skift af ejerskab. En e-mail er blevet sendt til %s for sidste godkendelse.Tak!%s ødelagde min webside%s virkede ikke%s virkede ikke som forventet%s er godt, men jeg har brug for en specifik feature, som ikke understøttes%s virker ikke%s stoppede pludseligt med at virkeOpgraderingen af %s blev fuldendt.TemaTemaskiftTemaerEn ny version af %s er tilgængelig.Dette plugin er ikke markeret som kompatibel med din nuværende version af WordPress.Dette plugin er ikke blevet testet med din nuværende version af WordPress.TidsstempelTitelTotalByPrøveperiodeTypeUbegrænsede licenserUbegrænsede opdateringerUbegrænset provision.Op til %s webstederOpdaterOpdater licensOpdateringer, annonceringer, marketing, ingen spamOpgraderUpload og aktiver den downloadede versionW00tBrugerpanelBruger-IDBrugernøgleBrugereVærdiVerificeretVerificer e-mailVersion %s er blevet udgivet.Vis detaljerVis betalte featuresAdvarselVi kan ikke finde nogen aktive licenser knyttet til den e-mailadresse, er du sikker på, det er den rigtige adresse?Vi kunne ikke finde din e-mailadresse i systemet, er du sikker på, det er den rigtige adresse?Vi har foretaget nogle rettelser til %s, %sWebsted, e-mail, og statistikker for sociale medier (valgfrit)Hvad forventede du?Hvilken feature?Angiv venligst %s?Hvilken pris ville du foretrække at betale?Hvad ledte du efter?Hvad er navnet på %s?Hvor vil du promovere %s?WordPress.org Plugin-sideVil du fortsætte med opdateringen?JaJa - %sDu har allerede brugt din prøveperiode.Du er 1 klik fra at begynde din %1$s dages gratis prøveperiode af planen %2$s.Det var det!Du benytter allerede %s under en prøveperiode.Du mangler kun ét skridt - %sDu har ikke en gyldig licens til at benytte premium-versionen.Du har en %s licens.Du har købt en licens til %s.Opdatering af %s blev gennemført.Du har måske overset det, men du behøver ikke at dele data og kan blot %s tilmeldingen.Du er allerede tilmeldt vores brugssporing, hvilket hjælper os med at forbedre %s.Du er allerede tilmeldt vores brugssporing, hvilket hjælper os med at forbedre dem.Din gratis prøveperiode for %s er blevet annulleret.Din konto blev aktiveret med planen %s.Din e-mailadresse er blevet verificeret - du er FOR SEJ!Din gratis prøveperiode er udløbet. Du kan stadig benytte alle de gratis features.Din licens er blevet annulleret. Hvis du mener, dette er en fejl, så kontakt venligst support.Din licens er udløbet. %1$sOpgrader nu%2$s for at fortsætte med at benytte %3$s uden forstyrrelser.Din licens er udløbet. Du kan stadig benytte alle funktionerne i %s, men du bliver nødt til at fornye din licens for at få opdateringer og support.Din licens er udløbet. Du kan stadig fortsætte med at benytte den gratis udgave af %s.Din licens er blevet aktiveret.Din licens blev deaktiveret, du er tilbage på planen %s.Dit navn er blevet opdateret.Din plan er blevet aktiveret.Din plan er blevet ændret til %s.Din plan er blevet opgraderet.Din prøveperiode er begyndt.ZIP / PostnummerSådanaktiver en licens herAktiv%s virker ikke uden %s.%s virker ikke uden pluginnet.Se hertillad%s tilbageAktivererårAPIFjernFejlfindingTillykkeBlokeretForbundetDownload senesteDownload seneste gratis versionMånedligtUdløberStiSender e-mailmdÅrligtÅrligtEngangsbeløbPlanIngen privat nøgleSDK-versionerLicensSynkroniserSynkroniser licensForfatterFraTilbaseret på %sStart gratis prøveperiodeFjernFjerndagedeaktivererdelegérsend %sIKKE%s sikkerheds- og feature-opdateringer, informativt indhold og tilbud.%s PlanFaktureret %sBedsteHeyUpsHey %s,timetimerInstalleretYee-hawlicensWebstedermsny beta-versionny versionikke verificeretPrisPriservalgfritVersionprodukterseksend mig sikkerheds- og feature-opdateringer, informativt indhold og tilbud.spring overHmmstart prøveperiodenabonnementskifterden seneste version af %s herprøveperiodePrøveperiodeSletNedgraderRedigerSkjulTilmeldFrameldKøbVisSpring overOpdaterOpgrader%s sidenfreemius/languages/freemius-cs_CZ.mo000064400000107044147600046700013516 0ustar00,,A-nohSG%  6*_#? c }      @ + !5!9!X!x!!!! !!!!!! ""("/"57"m" u"'"" ""o"K#GR###c$2v$!$$$$$%%#%,%4% 9%G% Y%e%%%% % % %%%Y&]&l& }&&&&&(&1&%':A'|'''' ' '''' '' ( (("(<(R(h(q((( (( ((_(aC))))) ) ) ***O+f+ k+ v+ ++j+ , ,#,,,:@,U{,,,-)!--K-y--'--7->.pG... ... /%/E/ M/&W/1~/./O/2/0b000B0,0 1 1 1+1I1 b1m1t1|1 1 11 111 111112 2 2#2=2,B2o2s2!22 22222 23 33363 <3H3 Q3 \3)j3V33444F45K4(444-44U 56a515~5I6h6o6 66(6*6"617+A7&m777777778 8 88 /8=8L8 e8s88888888 88 859l99&9999: : :: :=*:Lh:f:; ";.;?; E; Q;^; f;t; ;;;><%</<+=)K= u= =\===>C/>s>>>d/?-?? ??'?@ @$@*@@@@@E@'A:ALA[AbA*qAA*A^A.B=BEBKBdQBB BB BBCC6CKCcC ~CCCWC"CB!D6dD=DD DD- E;EYE/oEE*EEE$EF>6FuF F&F9F<GbKGPGUG_UHHKPI(IGI# J%1J)WJ$JUJ)J&K;8K6tKKKKK$L'LAL]LyL&L*L7L!M8MVM3oMMMMMM*N1BNtNN#NNNN OO)O >OKONTOOOOO PP1-P_PgP{PP P P P PP PCP5QQ:QQ Q QQQQ QQ R R R "R /R =R GR QR ]R jR;wRXWe XerXX,X !Y /Y;YDY4WYYbfZZ!Z [ [ ([6[ ;[ F[P[Y[Wb[6[[[[\\\ "\ ,\9\L\`\{\\ \\\\D\"])] 1]R] d]r]{]^O ^]^x^-_.F_u___ __ __ ``` $`1`E`*T`````` ` ``ta}aa aaaa"a8a;0b=lbbbbbbbbccc !c-c Ac LcYcac|cc cc ccdd d2d^8deddee 4e>eMe]ede%jeWfff g g!gj0g g ggg%gmgjh0h$hh%hi5i!Jili.oiSiwijjpj jjjj/jk k.k!Kk+mkBk&kll3l]:lGl llm)m"uu*Xv vvv vvvv vAvy@www 2x %*Pa0t!Ń,!9:t"/ʄ61_<gQ+F>-+5߈*Q@2ʼn$ &1DHL S ] i v, ʊ֊ߊ  %5 JT\k Ë]͋+4 C NY_nt| ÌWŌ )- F S_ zǍ ٍ   %s to access version %s security & feature updates, and support. The paid version of %1$s is already installed. Please activate it to start benefiting the %2$s features. %3$s%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.%s - plugin name. As complete "PluginX" activation nowComplete "%s" Activation Now%s Add-on was successfully purchased.%s Installs%s Licenses%s ago%s and its add-ons%s commission when a customer purchases a new license.%s free trial was successfully cancelled. Since the add-on is premium only it was automatically deactivated. If you like to use it in the future, you'll have to purchase a license.%s is a premium only add-on. You have to purchase a license first before activating the plugin.%s is the new owner of the account.%s minimum payout amount.%s or higher%s rating%s ratings%s sec%s star%s stars%s time%s times%s to access version %s security & feature updates, and support.%s to activate the license once you get it.APIASCII arrow left icon←ASCII arrow right icon➤AccountAccount DetailsActionsActivateActivate %sActivate %s PlanActivate %s featuresActivate Free VersionActivate LicenseActivate this add-onActivatedAdd Ons for %sAdd-OnAdd-OnsAdd-on must be deployed to WordPress.org or Freemius.AddressAffiliateAfter your free %s, pay as little as %sAgree & Activate LicenseAll TypesAllow & ContinueAlternatively, you can skip it for now and activate the license later, in your %s's network-level Account page.AmountAn unknown error has occurred while trying to set the user's beta mode.An unknown error has occurred.An update to a Beta version will replace your installed version of %s with the latest Beta release - use with caution, and not on production sites. You have been warned.Anonymous feedbackAre you sure you want to delete all Freemius data?Are you sure you want to proceed?Auto renews in %sAverage RatingAwesomeBecome an affiliateBillingBilling & InvoicesBlockingBlog IDBodyBusiness nameBuy a license nowBuy licenseCan't find your license key?CancelCancel %s & ProceedCancel SubscriptionCancel TrialCancelledCancelling %sCancelling %s...Cancelling the subscriptionCancelling the trial will immediately block access to all premium features. Are you sure?Change LicenseChange OwnershipChange PlanCheckoutCityClear API CacheClear Updates TransientsClick here to use the plugin anonymouslyClick to see reviews that provided a rating of %sClick to view full-size screenshot %dClone resolution admin notice products list labelProductsCodeCompatible up toContactContact SupportContact UsContributorsCouldn't activate %s.CountryDateDeactivateDeactivate LicenseDeactivationDebug LogDetailsDon't have a license key?Donate to this pluginDowngrading your planDownloadDownload %s VersionDownload the latest %s versionDownload the latest versionDownloadedEmailEmail addressEndEnter the email address you've used during the purchase and we will resend you the license key.Enter the email address you've used for the upgrade below and we will resend you the license key.Enter the new email addressErrorError received from the server:ExpiredExpires in %sExtra DomainsFileFilterFor compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.For delivery of security & feature updates, and license management, %s needs toFreeFree TrialFree versionFreemius APIFreemius DebugFreemius SDK couldn't find the plugin's main file. Please contact sdk@freemius.com with the current error.Freemius StateFull nameFunctionHave a license key?Homepage URL & title, WP & PHP versions, and site languageHow do you like %s so far? Test all our %s premium features with a %d-day free trial.How to upload and activate?How will you promote us?I can't pay for it anymoreI couldn't understand how to make it workI don't like to share my information with youI found a better %sI no longer need the %sI only needed the %s for a short periodIDIf you have a moment, please let us know why you are %sIf you skip this, that's okay! %1$s will still work just fine.If you'd like to use the %s on those sites, please enter your license key below and click the activation button.In %sInstall Free Version NowInstall NowInstall Update NowInstalling plugin: %sInvalid module ID.Invalid new user ID or email address.InvoiceIs ActiveIs active, deactivated, or uninstalledIt looks like the license could not be activated.It looks like the license deactivation failed.It looks like you are not in trial mode anymore so there's nothing to cancel :)It's a temporary %s - I'm troubleshooting an issueIt's not what I was looking forJoin the Beta programKeyKindly share what didn't work so we can fix it for future users...Kindly tell us the reason so we can improve.LastLast UpdatedLast licenseLatest Free Version InstalledLatest Version InstalledLearn moreLengthLicenseLicense AgreementLicense IDLicense KeyLicense issues?License keyLicense key is empty.LifetimeLocalhostLogLoggerMessageMethodModuleModule PathModule TypeMore information about %sNameNames, slugs, versions, and if active or notNewNew Version AvailableNewer Free Version (%s) InstalledNewer Version (%s) InstalledNewsletterNextNoNo IDNo credit card requiredNo expirationO.KOpt InOpt OutOpt in to make "%s" better!OtherOwner EmailOwner IDOwner NamePCI compliantPaid add-on must be deployed to Freemius.Part of the message telling the user what they should receive via email.a license keyPayPal account email addressPaymentsPayouts are in USD and processed monthly via PayPal.PlanPlan %s do not exist, therefore, can't start a trial.Plan %s does not support a trial period.Plan IDPlease contact us herePlease contact us with the following message:Please download %s.Please enter the license key that you received in the email right after the purchase:Please enter the license key to enable the debug mode:Please follow these steps to complete the upgradePlease let us know if you'd like us to contact you for security & feature updates, educational content, and occasional offers:Please provide your full name.PluginPlugin HomepagePlugin IDPlugin InstallPlugin installer section titleChangelogPlugin installer section titleDescriptionPlugin installer section titleFAQPlugin installer section titleFeatures & PricingPlugin installer section titleInstallationPlugin installer section titleReviewsPluginsPlugins & Themes SyncPremiumPremium versionPremium version already active.Privacy PolicyProceedProductsProvincePublic KeyPurchase LicensePurchase MoreQuick FeedbackRe-send activation emailRenew licenseRenew your license nowRequestsRequires WordPress VersionResultSDKSDK PathSave %sScheduled CronsScreenshotsSearch by addressSecret KeySecure HTTPS %s page, running from an external domainSeems like we are having some temporary issue with your trial cancellation. Please try again in few minutes.Seems like you got the latest release.Select CountrySend License KeySingle Site LicenseSite IDSitesSkip & %sSlugSnooze & %sSo you can reuse the license when the %s is no longer active.Sorry for the inconvenience and we are here to help if you give us a chance.Sorry, we could not complete the email update. Another user with the same email is already registered.StartStart TrialStart my free %sStateSubmit & %sSubscriptionSupportSupport ForumSync Data From ServerTax / VAT IDTerms of ServiceThank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.Thank you for applying for our affiliate program, we'll review your details during the next 14 days and will get back to you with further information.Thank you for updating to %1$s v%2$s!Thank you so much for using %s and its add-ons!Thank you so much for using %s!Thank you so much for using our products!Thank you!Thanks %s!Thanks for confirming the ownership change. An email was just sent to %s for final approval.The %s broke my siteThe %s didn't workThe %s didn't work as expectedThe %s is great, but I need specific feature that you don't supportThe %s is not workingThe %s suddenly stopped workingThe installation process has started and may take a few minutes to complete. Please wait until it is done - do not refresh this page.The remote plugin package does not contain a folder with the desired slug and renaming did not work.The upgrade of %s was successfully completed.ThemeTheme SwitchThemesThere is a new version of %s available.This will allow %s toTimestampTitleTo enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your "My Profile" section of your User Dashboard:TotalTownTrialTypeUnable to connect to the filesystem. Please confirm your credentials.Unlimited LicensesUnlimited UpdatesUp to %s SitesUpdateUpdate LicenseUpdates, announcements, marketing, no spamUpgradeUpload and activate the downloaded versionUsed to express elation, enthusiasm, or triumph (especially in electronic communication).W00tUser DashboardUser IDUsersValueVerification mail was just sent to %s. If you can't find it after 5 min, please check your spam box.VerifiedVerify EmailVersion %s was released.View %s StateView Basic %s InfoView Basic Profile InfoView Basic Website InfoView Diagnostic InfoView License EssentialsView Plugins & Themes ListView detailsView paid featuresWarningWe couldn't find your email address in the system, are you sure it's the right address?We made a few tweaks to the %s, %sWe're excited to introduce the Freemius network-level integration.Website, email, and social media statistics (optional)Welcome to %s! To get started, please enter your license key:What did you expect?What feature?What is your %s?What price would you feel comfortable paying?What you've been looking for?What's the %s's name?WordPress & PHP versions, site language & titleWordPress.org Plugin PageWould you like to proceed with the update?YesYes - %sYou already utilized a trial before.You are just one step away - %sYou do not have a valid license to access the premium version.You have a %s license.You have purchased a %s license.You have successfully updated your %s.Your account was successfully activated with the %s plan.Your email has been successfully verified - you are AWESOME!Your free trial has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your free trial has expired. You can still continue using all our free features.Your license has been cancelled. If you think it's a mistake, please contact support.Your license has expired. %1$sUpgrade now%2$s to continue using the %3$s without interruptions.Your license has expired. You can still continue using all the %s features, but you'll need to renew your license to continue getting updates and support.Your license has expired. You can still continue using the free %s forever.Your license was successfully activated.Your license was successfully deactivated, you are back to the %s plan.Your name was successfully updated.Your plan was successfully activated.Your plan was successfully changed to %s.Your plan was successfully upgraded.Your subscription was successfully cancelled. Your %s plan license will expire in %s.Your trial has been successfully started.ZIP / Postal CodeaddonX cannot run without pluginY%s cannot run without %s.addonX cannot run...%s cannot run without the plugin.allowas 5 licenses left%s leftas activating pluginActivatingas annual periodyearas application program interfaceAPIas close a windowDismissas code debuggingDebuggingas congratulationsCongratsas connection blockedBlockedas connection was successfulConnectedas download latest versionDownload Latestas download latest versionDownload Latest Free Versionas every monthMonthlyas expiration dateExpirationas file/folder pathPathas in the process of sending an emailSending emailas monthly periodmoas once a yearAnnualas once a yearAnnuallyas once a yearOnceas product pricing planPlanas secret encryption key missingNo Secretas software development kit versionsSDK Versionsas software licenseLicenseas synchronizeSyncas synchronize licenseSync Licenseas the plugin authorAuthoras turned offOffas turned onOnbased on %sclose a windowDismissclose windowDismissdeactivatingdelegatedo %sNOT%s send me security & feature updates, educational content and offers.e.g. Professional Plan%s Plane.g. billed monthlyBilled %se.g. the best productBestexclamationHeyexclamationOopsgreetingHey %s,interjection expressing joy or exuberanceYee-hawlicenselike websitesSitesmillisecondsmsnew Beta versionnew versionnot verifiednounPricenounPricingproduct versionVersionsecondssecsend me security & feature updates, educational content and offers.skipsomething somebody says when they are thinking about what you have just said.Hmmstart the trialsubscriptionswitchingthe latest %s version heretrialtrial periodTrialverbDeleteverbDowngradeverbEditverbHideverbOpt InverbOpt OutverbPurchaseverbShowverbSkipverbUpdateverbUpgradex-ago%s agoProject-Id-Version: WordPress SDK Report-Msgid-Bugs-To: https://github.com/Freemius/wordpress-sdk/issues PO-Revision-Date: 2015-10-01 21:38+0000 Last-Translator: Karolína Vyskočilová , 2019-2023 Language-Team: Czech (Czech Republic) (http://app.transifex.com/freemius/wordpress-sdk/language/cs_CZ/) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Language: cs_CZ Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3; X-Poedit-Basepath: .. X-Poedit-KeywordsList: get_text_inline;get_text_x_inline:1,2c;$this->get_text_inline;$this->get_text_x_inline:1,2c;$this->_fs->get_text_inline;$this->_fs->get_text_x_inline:1,2c;$this->fs->get_text_inline;$this->fs->get_text_x_inline:1,2c;$fs->get_text_inline;$fs->get_text_x_inline:1,2c;$this->_parent->get_text_inline;$this->_parent->get_text_x_inline:1,2c;fs_text_inline;fs_echo_inline;fs_esc_js_inline;fs_esc_attr_inline;fs_esc_attr_echo_inline;fs_esc_html_inline;fs_esc_html_echo_inline;fs_text_x_inline:1,2c;fs_echo_x_inline:1,2c;fs_esc_attr_x_inline:1,2c;fs_esc_js_x_inline:1,2c;fs_esc_js_echo_x_inline:1,2c;fs_esc_html_x_inline:1,2c;fs_esc_html_echo_x_inline:1,2c X-Poedit-SearchPath-0: . X-Poedit-SearchPathExcluded-0: *.js X-Poedit-SourceCharset: UTF-8 %s pro přístup k verzi %s zajišťující podporu a nejen bezpečnostní aktualizace. Placená verze %1$s je již nainstalována. Aktivujte jí, abyste mohli těžit z %2$s funkcí. %3$s%1s okamžitě zastaví všechny budoucí opakující se platby a licence k plánu %s vyprší za %s.Dokončit aktivaci „%s“Rozšíření %s bylo úspěšně zakoupeno.%s instalací%s licencíPřed %s%s a jeho doplňky%s provizi, když zákazník zakoupí novou licenci.%s bezplatná zkušební verze byla úspěšně zrušena. Jelikož toto rozšíření nenabízí bezplatnou verzi, bylo automaticky deaktivováno. Chcete-li jej v budoucnu používat, budete si muset zakoupit licenci.%s je pouze prémiové rozšíření. Před aktivací pluginu si musíte nejprve zakoupit licenci.%s je nový vlastník účtu.%s minimální částka výplaty.%s nebo vyšší%s hodnocení%s hodnocení%s s%s hvězda%s hvězd%s krát%s krát%s pro přístup k verzi %s zajišťující podporu a nejen bezpečnostní aktualizace.%s, abyste mohli aktivovat licenci, až ji obdržíte.API←➤ÚčetDetaily účtuAkceAktivovatAktivovat %sAktivovat %s plánAktivovat %s funkceAktivovat bezplatnou verziAktivovat licenciAktivovat toto rozšířeníAktivovanýRozšíření pro %sDoplněkDoplňkyRozšíření musí být nasazeno na WordPress.org nebo na Freemius.AdresaPartnerPo bezplatné %s platit jen v %sAktivovat licenciVšechny typyPovolit a pokračovatPřípadně můžete aktivaci licence prozatím přeskočit a provést ji později na stránce účtu na úrovni sítě %s.ČástkaBěhem nastavování uživatelského beta módu došlo k neočekávané chybě.Došlo k neznámé chybě.Aktualizováním na Beta verzi nahradíte nainstalovanou verzi %s nejnovějším vydáním Beta verze - používejte s opatrností a ne na produkčních webech. Varovali jsme vás.Anonymní zpětná vazbaOpravdu chcete smazat veškerá Freemius data?Opravdu chcete pokračovat?Automaticky se obnoví za %sPrůměrné hodnoceníÚžasnéStaňte se naším afiliátemFakturaceFakturace a platbyBlokováníBlog IDTěloJméno firmyKoupit licenci teďKoupit licenciNemůžete najít svůj licenční klíč?ZrušitZrušit %s > pokračovatZrušit předplatnéZrušit zkušební verziZrušenaRuším %sRuším %s...Ruším předplatnéZrušení zkušební verze okamžitě zablokuje přístup ke všem prémiovým funkcím. Opravdu chcete pokračovat?Změnit licenciZměnit vlastnictvíZměnit plánPokladnaMěstoVyčistit API cacheVyčistit transitenty aktualizacíKlikněte zde pro anonymní používání tohoto pluginuKliknutím zobrazíte recenze, které stojí za hodnocenímKlikněte pro zobrazení plné velikosti snímku obrazovky %dProduktyKódKompatibilní až poKontaktKontaktovat podporuSupportPřispěvateléNelze aktivovat %s.ZeměDatumDeaktivovatDeaktivovat licenciDeaktivaceLadící logDetailyNemáte licenční klíč?Přispějte na tento pluginSnižuji vaše předplatnéStáhnoutStáhnout verzi %sStáhnout nejnovější verzi %sStáhnout nejnovější verziStaženoE-mailE-mailová adresaKonecZadejte e-mail, který jste použili při nákupu, a my vám znovu zašleme licenční klíč.Zadejte e-mail použitý při objednávce, abychom vám na něj mohli znovu poslat licenční klíč.Zadejte nový e-mailChybaChyba přijatá ze serveru:VypršeloVyprší za %sDalší doménySouborFiltrAby bylo vyhověno WordPress.org pokynům, před zahájením zkušebního období vás žádáme, abyste s námi sdíleli data o uživateli a necitlivé informace o webu, to umožní %s periodicky odesílat data do %s za účelem kontroly aktualizací a ověření nároku na zkušební verzi.Pro poskytování aktualizací zabezpečení a funkcí a správu licencí %s potřebujeZdarmaZkušební verze zdarmaVerze zdarmaFreemius APIFreemius DebugFreemius SDK nemohl najít hlavní soubor pluginu. S aktuální chybou se obraťte se na sdk@freemius.com.Stav FreemiusCelé jménoFunkceMáte licenční klíč?URL webu, verze WP a PHP a jazyk webuJak se vám líbí %s? Otestujte všechny naše %s nadstandardní funkce s %d-denní zkušební verze zdarma.Jak nahrát a aktivovat?Jakým způsobem budete mé produkty propagovat?je drahý a nemohu si ho už dovolitNedokázal jsem jej zprovoznitNechci s vámi sdílet své informacenašel jsem lepší %sjiž nepotřebuji %spotřeboval jsem %s jen dočasněIDMáte-li chvilku, dejte nám vědět, proč %sPokud tuto část přeskočíte, nevadí! %1$s bude stále fungovat bez problémů.Pokud chcete použít %s na těchto stránkách, zadejte platný licenční klíč a klikněte na tlačítko aktivovat.Za %sNainstalovat verzi zdarmaInstalovatNainstalovat aktualizaciInstaluji plugin: %sNeplatné ID modulu.Neplatné ID uživatele nebo e-mailová adresa.FakturaJe aktivníJe aktivní, deaktivovaný nebo odinstalovanýLicenci se nepodařilo aktivovat.Deaktivace licence pravděpodobně selhala.Zkušební režim už vám skončil, takže už není co rušit :)dočasná %s - ladím problém na webuNení to to, co jsem hledalOdebírat betaverzeKlíčDejte nám prosím vědět, co nefungovalo, ať to můžeme opravit pro další uživatele...Dejte nám prosím vědět z jakého důvodu, ať to můžeme zlepšit.PosledníPoslední aktualizacePoslední licenceNainstalována nejnovější verze zdarmaNainstalována nejnovější verzePřečtěte si víceDélkaLicenceLicenční smlouvaLicenční IDLicenční klíčProblémy s licencí?Licenční klíčLicenční klíč je prázdný.DoživotníLocalhostZáznamLoggerZprávaMetodaModulCesta k moduluTyp moduluVíce informací o %sJménoJména, slugy, verze a jestli jsou nebo nejsou aktivníNovýNová verze k dispoziciNovější verze zdarma (%s) nainstalovánaNovější verze (%s) nainstalovánaNewsletterNásledujícíNeŽádné IDKreditní karta není vyžadovánaBez vypršeníOKOdebírat novinkyOdhlásit seZúčastněte se, aby byl "%s" ještě lepší!jinéE-mail vlastníkaID vlastníkaJméno vlastníkaKompatibilní s PCIPlacený doplněk musí být nasazen na Freemius.licenční klíčE-mailová adresa účtu PayPalPlatbyVyplácení probíhá přes PayPal jednou za měsíc a v USD.Druh členstvíPlán %s neexistuje, proto nemůžete použít zkušební verzi.Plán %s nepodporuje zkušební období.ID členstvíKontaktujte nás prosím zdeKontaktujte nás prosím s následující zprávou:Stáhněte si prosím %s.Zadejte licenční klíč, který najdete v e-mailu odeslaném po provedení objednávky:Pro povolení módu ladění chyb zadejte licenční klíč.Dokončete upgrade provedením následujících krokůVyberte si, jestli chcete být informování o bezpečnostních aktualizacích, vylepšení funkcionality, vzdělávacímu obsahu nebo občasných obchodních nabídkách:Zadejte prosím jméno a příjmení.PluginHlavní stránka pluginuID pluginuInstalace pluginuHistorie změnPopisFAQVlastnosti a ceníkInstalaceVaše hodnoceníPluginyPluginy a synchronizace šablonPrémiumPrémiová verzePrémiová verze je již aktivní.Zásady ochrany osobních údajůPokračovatProduktyOkresVeřejný klíčKoupit licenciZakoupit dalšíRychlá zpětná vazbaZnovu poslat aktivační e-mailObnovit licenciObnovte svou licenci teďŽádostiVyžaduje verzi WordPressVýsledekSDKCesta l SDKUložit %sPlánované cronySnímky obrazovkyHledat podle adresyTajný klíčZabezpečená stránka HTTPS %s spuštěná z externí doményOmlouváme se, ale měli jsme nějaký dočasný problém se zrušením vaší zkušební licence. Zkuste to znovu za několik minut.Pravděpodobně máte nejnovější verzi.Vyberte zemiOdeslat licenční klíčLicence pro jednu instalaciID stránkyWebyPřeskočit & %sZkratkaOdložit & %sLicenci tak můžete znovu použít, když už %s není aktivní.Omlouváme se za způsobené nepříjemnosti, ale když se nám dáte šanci, tak se vám ze všech sil pokusíme pomoci.Omlouváme se, ale aktualizaci e-mailu jsem nemohli dokončit. Uživatel s vámi zadaným e-mailem už je registrován.ZačátekZačít zkušební verziZačít můj bezplatný %sKrajOdeslat & %sPředplatnéPodporaFórum podporySynchronizovat data ze serveruDIČPodmínky službyDěkujeme, že jste se přihlásili do našeho partnerského programu, ale bohužel jsme se v tuto chvíli rozhodli vaši žádost zamítnout. Zkuste to prosím znovu za 30 dní.Děkujeme, že jste se přihlásili do našeho partnerského programu, během následujících 14 dnů prověříme vaše údaje a ozveme se vám s dalšími informacemi.Děkujeme za aktualizaci na verzi %1$s v%2$s!Moc vám děkujeme za používání %s a jeho doplňků!Moc vám děkujeme za používání %s!Moc vám děkujeme za používání našich produktů!Děkujeme!Děkujeme %s!Děkujeme za potvrzení změny vlastnictví. E-mail byl právě odeslán na adresu %s, ke konečnému schválení.%s rozbil můj web%s nefungoval%s nefungoval podle očekávání%s je skvělý, ale potřebuji funkci, kterou není podporovaná%s nefunguje%s náhle přestal pracovatProces instalace byl zahájen a může trvat několik minut. Počkejte prosím na dokončení - neobnovujte tuto stránku.Balíček remote pluginů neobsahuje složku s žádoucím "slug" a přejmenování nefunguje.Aktualizace %s byla úspěšně dokončena.ŠablonaZměna šablonyŠablonyJe k dispozici nová verze %s.To dovolí %s Datum a časNadpisPro zapnutí módu ladění chyb zadejte tajný klíč vlastníka licence (uživatel s ID = %d), který najdete v sekci "Můj profil" vaší nástěnky.CelkemMěstoZkouškaTypNelze se připojit k souborovému systému. Potvrďte prosím své přístupové údaje.Neomezené množství instalacíNeomezené aktualizaceAž pro %s webůAktualizovatAktualizovat licenciAktualizace, oznámení, marketing, žádný spamUpgradeNahrát a aktivovat stáhnutou verziCože?Uživatelská nástěnkaID uživateleUživateléHodnotaOvěřovací zpráva byla právě odeslána na e-mail %s. Pokud ji nenajdete do 5 min, zkontrolujte prosím složku pro spam.OvěřenoOvěřit e-mailByla vydána verze %s.Zobrazit %s stavZobrazit základní informace o %sZobrazit základní informace o profiluZobrazit základní informace o stránceZobrazit diagnostické informaceZobrazit základní informace k licenciZobrazit seznam pluginů a šablonZobrazit podrobnostiZobrazit placené funkceVarováníNemohli jsme najít vaši e-mailovou adresu v systému, jste si jisti, že je to správná adresa?Udělali jsme několik vylepšení %s, %sJsme rádi, že vám můžeme ukázat integraci Freemiusu i v rámci sítě webů.Statistika o webová stránc, emaiul a sociálních médiích%s vás vítá! Pro začátek zadejte svůj licenční klíč:Co jste očekávali?Jaká funkce?Jaké je vaše "%s"?Jakou cenu byste byli ochotni platit?Co jste hledali?Jak se %s jmenuje?Cerzi WordPressu a PHP, jazyk webu a jeho jménoNázev pluginu na WordPress.orgChcete pokračovat v aktualizaci?AnoAno - %sZkušební verzi jste již dříve využili.Jste jen na krok od - %sNemáte platnou licenci pro přístup k prémiové verzi.Máte licenci „%s“.Zakoupili jste licenci %s.Úspěšně jste aktualizovali %s.Účet byl úspěšně aktivován s %s plánem.Váš e-mail byl úspěšně ověřen - jste skvělý!Platnost bezplatné zkušební verze vypršela. %1$s Upgradujte nyní%2$s abyste mohli pokračovat v používání %3$s bez přerušení.Platnost bezplatné zkušební verze vypršela. Stále můžete pokračovat v používání všech našich bezplatných funkcí.Vaše licence byla zrušena. Pokud si myslíte, že je to chyba, obraťte se na naší podporu.Vaše licence vypršela. %1$sObnovte předplatné%2$s, abyste mohli mohli %3$s používat bez omezení.Vaše licence vypršela. Stále však můžete používat všechny funkce verze %s, ale pro získání technické podpory a nejnovějších aktualizací budete muset obnovit svou licenci.Vaše licence vypršela. Stále však můžete používat %s zdarma bez omezení.Vaše licence byla úspěšně aktivována.Vaše licence byla úspěšně deaktivována, jste zpět na plánu %s.Vaše jméno bylo úspěšně aktualizováno.Vaše licence byla úspěšně aktivována.Váše předplatné bylo úspěšně změněno na %s.Váš plán byl úspěšně aktualizován.Vaše předplatné bylo úspěšně zrušeno. Platnost licence %s vyprší za %s.Vaše zkušebí verze byla úspěšně spuštěna.PSČ / směrovací číslo%s nelze spustit bez %s.%s nelze spustit bez tohoto pluginu.povolitZbývá %sProbíhá aktivacerokAPISkrýtDebuggingGratulujemeZablokovánoPřipojenoStáhněte si nejnovějšíStáhněte si nejnovější bezplatnou verziMěsíčněExpiraceSložkaProbíhá odesílání e-mailůpoRočněRočněJedenkrátDruh členstvíTajný klíč chybíSDK verzeLicenceSynchronizovatSynchronizovat licenceAutorVypnutoZapnutozaloženo na %sSkrýtSkrýtdeaktivujetedelegovat%sneposílejte%s mi bezpečnostní aktualizace a vylepšení, vzdělávací obsah a nabídky.%s plánÚčtováno %sNejlepšíDobrý denJejdaDobrý den %s,HurálicenceWebymsnová Beta verzenová verzenení ověřenoCenaCeníkVerzesposílejte mi bezpečnostní aktualizace a vylepšení, vzdělávací obsah a nabídky.přeskočitHmmspustit zkušební verzipředplatnépřepínámnejnovější %s verze zdezkušebníZkušební verzeSmazatPřejít na nižší verziUpravitSkrýtOdebírat novinkyOdhlásit seZakoupitZobrazitPřeskočitAktualizovatVylepšitPřed %sfreemius/templates/account/partials/site.php000064400000047625147600046700015333 0ustar00get_slug(); $site = $VARS['site']; $main_license = $VARS['license']; $is_data_debug_mode = $fs->is_data_debug_mode(); $is_whitelabeled = $fs->is_whitelabeled(); $has_paid_plan = $fs->has_paid_plan(); $is_premium = $fs->is_premium(); $main_user = $VARS['user']; $blog_id = $site['blog_id']; $install = $VARS['install']; $is_registered = ! empty( $install ); $license = null; $trial_plan = $fs->get_trial_plan(); $free_text = fs_text_inline( 'Free', 'free', $slug ); if ( $is_whitelabeled && is_object( $install ) && $fs->is_delegated_connection( $blog_id ) ) { $is_whitelabeled = $fs->is_whitelabeled( true, $blog_id ); } ?> data-install-id="id ?>"> id ?>
    $fs, 'slug' => $slug, 'blog_id' => $blog_id, 'class' => 'button-small', ); $license = null; if ( $is_registered ) { $view_params['install_id'] = $install->id; $view_params['is_localhost'] = $install->is_localhost(); $has_license = FS_Plugin_License::is_valid_id( $install->license_id ); $license = $has_license ? $fs->_get_license_by_id( $install->license_id ) : null; } else { $view_params['is_localhost'] = FS_Site::is_localhost_by_address( $site['url'] ); } if ( ! $is_whitelabeled ) { if ( is_object( $license ) ) { $view_params['license'] = $license; // Show license deactivation button. fs_require_template( 'account/partials/deactivate-license-button.php', $view_params ); } else { if ( is_object( $main_license ) && $main_license->can_activate( $view_params['is_localhost'] ) ) { // Main license is available for activation. $available_license = $main_license; } else { // Try to find any available license for activation. $available_license = $fs->_get_available_premium_license( $view_params['is_localhost'] ); } if ( is_object( $available_license ) ) { $premium_plan = $fs->_get_plan_by_id( $available_license->plan_id ); $view_params['license'] = $available_license; $view_params['class'] .= ' button-primary'; $view_params['plan'] = $premium_plan; fs_require_template( 'account/partials/activate-license-button.php', $view_params ); } } } } ?> is_trial() ) { if ( is_object( $trial_plan ) && $trial_plan->id == $install->trial_plan_id ) { $plan_title = is_string( $trial_plan->name ) ? strtoupper( $trial_plan->title ) : fs_text_inline( 'Trial', 'trial', $slug ); } else { $plan_title = fs_text_inline( 'Trial', 'trial', $slug ); } } else { $plan = $fs->_get_plan_by_id( $install->plan_id ); $plan_title = strtoupper( is_string( $plan->title ) ? $plan->title : strtoupper( $free_text ) ); } } ?> > user_id != $main_user->id ) : ?> user_id ) ?> > > > > > > id != $license->id ) : ?> _get_subscription( $license->id ) ?> is_lifetime() && is_object( $subscription ) ) : ?> > is_active(); $renews_in_text = fs_text_inline( 'Auto renews in %s', 'renews-in', $slug ); /* translators: %s: Time period (e.g. Expires in "2 months") */ $expires_in_text = fs_text_inline( 'Expires in %s', 'expires-in', $slug ); ?>
    : license_id ) ) : ?> is_homepage_url_tracking_allowed( $blog_id ) ?>
    id}", ':' ) ) ?>
    : get_name() ) ?>
    : email ) ?>
    : id ?>
    : public_key ) ?>
    : secret_key ) ?>
    : get_html_escaped_masked_secret_key() ?>
    : id ?> - billing_cycle ? _fs_text_inline( 'Annual', 'annual', $slug ) : _fs_text_inline( 'Monthly', 'monthly', $slug ) ); ?> is_first_payment_pending() ) : ?> is_first_payment_pending() ) : ?> expiration ) ); $downgrade_confirmation_message = sprintf( $downgrade_x_confirm_text, ( $fs->is_only_premium() ? $cancelling_subscription_text : $downgrading_plan_text ), $plan->title, $human_readable_license_expiration ); $after_downgrade_message = ! $license->is_block_features ? sprintf( $after_downgrade_non_blocking_text, $plan->title, $fs->get_module_label( true ) ) : sprintf( $after_downgrade_blocking_text, $plan->title ); ?>
    freemius/templates/account/partials/index.php000064400000000127147600046700015460 0ustar00_get_subscription( $license->id ) : null; $has_active_subscription = ( is_object( $license_subscription ) && $license_subscription->is_active() ); $button_id = "fs_disconnect_button_{$fs->get_id()}"; $website_link = sprintf( '%s', fs_strip_url_protocol( untrailingslashit( Freemius::get_unfiltered_site_url() ) ) ); ?>
    esc_html_inline( 'Disconnect', 'disconnect' ) ?>
    freemius/templates/account/partials/deactivate-license-button.php000064400000002507147600046700021417 0ustar00
    freemius/templates/account/partials/addon.php000064400000052576147600046700015455 0ustar00get_slug(); $fs_blog_id = $VARS['fs_blog_id']; $active_plugins_directories_map = $VARS['active_plugins_directories_map']; $addon_info = $VARS['addon_info']; $is_addon_activated = $fs->is_addon_activated( $addon_id ); $is_addon_connected = $addon_info['is_connected']; $is_addon_installed = $VARS['is_addon_installed']; $fs_addon = ( $is_addon_connected && $is_addon_installed ) ? freemius( $addon_id ) : false; // Aliases. $download_latest_text = fs_text_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $slug ); $downgrading_plan_text = fs_text_inline( 'Downgrading your plan', 'downgrading-plan', $slug ); $cancelling_subscription_text = fs_text_inline( 'Cancelling the subscription', 'cancelling-subscription', $slug ); /* translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription' */ $downgrade_x_confirm_text = fs_text_inline( '%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.', 'downgrade-x-confirm', $slug ); $prices_increase_text = fs_text_inline( 'Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.', 'pricing-increase-warning', $slug ); $cancel_trial_confirm_text = fs_text_inline( 'Cancelling the trial will immediately block access to all premium features. Are you sure?', 'cancel-trial-confirm', $slug ); $after_downgrade_non_blocking_text = fs_text_inline( 'You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.', 'after-downgrade-non-blocking', $slug ); $after_downgrade_blocking_text = fs_text_inline( 'Once your license expires you can still use the Free version but you will NOT have access to the %s features.', 'after-downgrade-blocking', $slug ); /* translators: %s: Plan title (e.g. "Professional") */ $activate_plan_text = fs_text_inline( 'Activate %s Plan', 'activate-x-plan', $slug ); $version_text = fs_text_x_inline( 'Version', 'product version', 'version', $slug ); /* translators: %s: Time period (e.g. Auto renews in "2 months") */ $renews_in_text = fs_text_inline( 'Auto renews in %s', 'renews-in', $slug ); /* translators: %s: Time period (e.g. Expires in "2 months") */ $expires_in_text = fs_text_inline( 'Expires in %s', 'expires-in', $slug ); $cancel_trial_text = fs_text_inline( 'Cancel Trial', 'cancel-trial', $slug ); $change_plan_text = fs_text_inline( 'Change Plan', 'change-plan', $slug ); $upgrade_text = fs_text_x_inline( 'Upgrade', 'verb', 'upgrade', $slug ); $addons_text = fs_text_inline( 'Add-Ons', 'add-ons', $slug ); $downgrade_text = fs_text_x_inline( 'Downgrade', 'verb', 'downgrade', $slug ); $trial_text = fs_text_x_inline( 'Trial', 'trial period', 'trial', $slug ); $free_text = fs_text_inline( 'Free', 'free', $slug ); $activate_text = fs_text_inline( 'Activate', 'activate', $slug ); $plan_text = fs_text_x_inline( 'Plan', 'as product pricing plan', 'plan', $slug ); // Defaults. $plan = null; $is_paid_trial = false; /** * @var FS_Plugin_License $license */ $license = null; $site = null; $is_active_subscription = false; $subscription = null; $is_paying = false; $show_upgrade = false; $is_whitelabeled = $VARS['is_whitelabeled']; if ( is_object( $fs_addon ) ) { $is_paying = $fs_addon->is_paying(); $user = $fs_addon->get_user(); $site = $fs_addon->get_site(); $license = $fs_addon->_get_license(); $subscription = ( is_object( $license ) ? $fs_addon->_get_subscription( $license->id ) : null ); $plan = $fs_addon->get_plan(); $plan_name = $plan->name; $plan_title = $plan->title; $is_paid_trial = $fs_addon->is_paid_trial(); $version = $fs_addon->get_plugin_version(); $is_whitelabeled = ( $fs_addon->is_whitelabeled( true ) && ! $fs_addon->get_parent_instance()->is_data_debug_mode() ); $show_upgrade = ( ! $is_whitelabeled && $fs_addon->has_paid_plan() && ! $is_paying && ! $is_paid_trial && ! $fs_addon->_has_premium_license() ); } else if ( $is_addon_connected ) { if ( empty( $addon_info ) || ! isset( $addon_info['site'] ) ) { $is_addon_connected = false; } else { /** * @var FS_Site $site */ $site = $addon_info['site']; $version = $addon_info['version']; $plan_name = isset( $addon_info['plan_name'] ) ? $addon_info['plan_name'] : ''; $plan_title = isset( $addon_info['plan_title'] ) ? $addon_info['plan_title'] : ''; if ( isset( $addon_info['license'] ) ) { $license = $addon_info['license']; } if ( isset( $addon_info['subscription'] ) ) { $subscription = $addon_info['subscription']; } $has_valid_and_active_license = ( is_object( $license ) && $license->is_active() && $license->is_valid() ); $is_paid_trial = ( $site->is_trial() && $has_valid_and_active_license && ( $site->trial_plan_id == $license->plan_id ) ); $is_whitelabeled = $addon_info['is_whitelabeled']; } } $has_feature_enabled_license = ( is_object( $license ) && $license->is_features_enabled() ); $is_active_subscription = ( is_object( $subscription ) && $subscription->is_active() ); $show_delete_install_button = ( ! $is_paying && WP_FS__DEV_MODE && ! $is_whitelabeled ); ?> > id ?> is_trial() || is_object( $license ) ) : ?> is_trial() ) { $tags[] = array( 'label' => $trial_text, 'type' => 'success' ); $tags[] = array( 'label' => sprintf( ( $is_paid_trial ? $renews_in_text : $expires_in_text ), human_time_diff( time(), strtotime( $site->trial_ends ) ) ), 'type' => ( $is_paid_trial ? 'success' : 'warn' ) ); } else { if ( is_object( $license ) ) { if ( $license->is_cancelled ) { $tags[] = array( 'label' => fs_text_inline( 'Cancelled', 'cancelled', $slug ), 'type' => 'error' ); } else if ( $license->is_expired() ) { $tags[] = array( 'label' => fs_text_inline( 'Expired', 'expired', $slug ), 'type' => 'error' ); } else if ( $license->is_lifetime() ) { $tags[] = array( 'label' => fs_text_inline( 'No expiration', 'no-expiration', $slug ), 'type' => 'success' ); } else if ( ! $is_active_subscription && ! $license->is_first_payment_pending() ) { $tags[] = array( 'label' => sprintf( $expires_in_text, human_time_diff( time(), strtotime( $license->expiration ) ) ), 'type' => 'warn' ); } else if ( $is_active_subscription && ! $subscription->is_first_payment_pending() ) { $tags[] = array( 'label' => sprintf( $renews_in_text, human_time_diff( time(), strtotime( $subscription->next_payment ) ) ), 'type' => 'success' ); } } } foreach ( $tags as $t ) { printf( '' . "\n", $t['type'], $t['label'] ); } ?> get_id(), 'account', 'deactivate_license', fs_text_inline( 'Deactivate License', 'deactivate-license', $slug ), '', array( 'plugin_id' => $addon_id ), false, true ); $after_downgrade_message = ! $license->is_block_features ? sprintf( $after_downgrade_non_blocking_text, $plan->title, $fs_addon->get_module_label( true ) ) : sprintf( $after_downgrade_blocking_text, $plan->title ); if ( ! $license->is_lifetime() && $is_active_subscription ) { $human_readable_license_expiration = human_time_diff( time(), strtotime( $license->expiration ) ); $downgrade_confirmation_message = sprintf( $downgrade_x_confirm_text, ( $fs_addon->is_only_premium() ? $cancelling_subscription_text : $downgrading_plan_text ), $plan->title, $human_readable_license_expiration ); $buttons[] = fs_ui_get_action_button( $fs->get_id(), 'account', 'downgrade_account', esc_html( $fs_addon->is_only_premium() ? fs_text_inline( 'Cancel Subscription', 'cancel-subscription', $slug ) : $downgrade_text ), '', array( 'plugin_id' => $addon_id ), false, false, false, ( $downgrade_confirmation_message . ' ' . $after_downgrade_message . ' ' . $prices_increase_text ), 'POST' ); } } else if ( $is_paid_trial ) { $buttons[] = fs_ui_get_action_button( $fs->get_id(), 'account', 'cancel_trial', esc_html( $cancel_trial_text ), '', array( 'plugin_id' => $addon_id ), false, false, 'dashicons dashicons-download', $cancel_trial_confirm_text, 'POST' ); } else if ( ! $has_feature_enabled_license ) { $premium_licenses = $fs_addon->get_available_premium_licenses(); if ( ! empty( $premium_licenses ) ) { $premium_license = $premium_licenses[0]; $has_multiple_premium_licenses = ( 1 < count( $premium_licenses ) ); if ( ! $has_multiple_premium_licenses ) { $premium_plan = $fs_addon->_get_plan_by_id( $premium_license->plan_id ); $site = $fs_addon->get_site(); $buttons[] = fs_ui_get_action_button( $fs->get_id(), 'account', 'activate_license', esc_html( sprintf( $activate_plan_text, $premium_plan->title, ( $site->is_localhost() && $premium_license->is_free_localhost ) ? '[localhost]' : ( 1 < $premium_license->left() ? $premium_license->left() . ' left' : '' ) ) ), ($has_multiple_premium_licenses ? 'activate-license-trigger ' . $fs_addon->get_unique_affix() : ''), array( 'plugin_id' => $addon_id, 'license_id' => $premium_license->id, ), true, true ); $is_license_activation_added = true; } } } } // if ( 0 == count( $buttons ) ) { if ( $fs_addon->is_premium() && ! $is_license_activation_added ) { $fs_addon->_add_license_activation_dialog_box(); $buttons[] = fs_ui_get_action_button( $fs->get_id(), 'account', 'activate_license', ( ! $has_feature_enabled_license ) ? fs_esc_html_inline( 'Activate License', 'activate-license', $slug ) : fs_esc_html_inline( 'Change License', 'change-license', $slug ), 'activate-license-trigger ' . $fs_addon->get_unique_affix(), array( 'plugin_id' => $addon_id, ), (! $has_feature_enabled_license), true ); $is_license_activation_added = true; } if ( $fs_addon->has_paid_plan() ) { // Add sync license only if non of the other CTAs are visible. $buttons[] = fs_ui_get_action_button( $fs->get_id(), 'account', $fs->get_unique_affix() . '_sync_license', fs_esc_html_x_inline( 'Sync', 'as synchronize', 'sync', $slug ), '', array( 'plugin_id' => $addon_id ), false, true ); } // } } else if ( ! $show_upgrade ) { if ( $fs->is_addon_installed( $addon_id ) ) { $addon_file = $fs->get_addon_basename( $addon_id ); if ( ! isset( $active_plugins_directories_map[ dirname( $addon_file ) ] ) ) { $buttons[] = sprintf( '%s', wp_nonce_url( 'plugins.php?action=activate&plugin=' . $addon_file, 'activate-plugin_' . $addon_file ), fs_esc_attr_inline( 'Activate this add-on', 'activate-this-addon', $slug ), $activate_text ); } } else { if ( $fs->is_allowed_to_install() ) { $buttons[] = sprintf( '%s', wp_nonce_url( self_admin_url( 'update.php?' . ( ( isset( $addon_info['has_paid_plan'] ) && $addon_info['has_paid_plan'] ) ? 'fs_allow_updater_and_dialog=true&' : '' ) . 'action=install-plugin&plugin=' . $addon_info['slug'] ), 'install-plugin_' . $addon_info['slug'] ), fs_text_inline( 'Install Now', 'install-now', $slug ) ); } else { $buttons[] = sprintf( '%s', $fs->_get_latest_download_local_url( $addon_id ), esc_html( $download_latest_text ) ); } } } if ( $show_upgrade ) { $buttons[] = sprintf( ' %s', esc_url( network_admin_url( 'plugin-install.php?fs_allow_updater_and_dialog=true' . ( ! empty( $fs_blog_id ) ? '&fs_blog_id=' . $fs_blog_id : '' ) . '&tab=plugin-information&parent_plugin_id=' . $fs->get_id() . '&plugin=' . $addon_info['slug'] . '&TB_iframe=true&width=600&height=550' ) ), esc_attr( sprintf( fs_text_inline( 'More information about %s', 'more-information-about-x', $slug ), $addon_info['title'] ) ), esc_attr( $addon_info['title'] ), ( $fs_addon->has_free_plan() ? $upgrade_text : fs_text_x_inline( 'Purchase', 'verb', 'purchase', $slug ) ) ); } $buttons_count = count( $buttons ); ?> 1 ) : ?>
    1 ) : ?>
    is_addon_installed( $addon_id ); ?> get_addon_basename( $addon_id ) ?> is_allowed_to_install() ) : ?> get_id(), 'account', 'delete_account', fs_text_x_inline( 'Delete', 'verb', 'delete', $slug ), '', array( 'plugin_id' => $addon_id ), false, $show_upgrade ); } ?> freemius/templates/account/partials/activate-license-button.php000064400000004466147600046700021114 0ustar00
    freemius/templates/account/payments.php000064400000003334147600046700014375 0ustar00get_slug(); ?>

    >
    id ?> created ) ) ?> formatted_gross() ?> is_migrated() ) : ?>
    get_slug(); $edit_text = fs_text_x_inline( 'Edit', 'verb', 'edit', $slug ); $update_text = fs_text_x_inline( 'Update', 'verb', 'update', $slug ); $billing = $fs->_fetch_billing(); $has_billing = ( $billing instanceof FS_Billing ); if ( ! $has_billing ) { $billing = new FS_Billing(); } ?>

    > 'Afghanistan', 'AX' => 'Aland Islands', 'AL' => 'Albania', 'DZ' => 'Algeria', 'AS' => 'American Samoa', 'AD' => 'Andorra', 'AO' => 'Angola', 'AI' => 'Anguilla', 'AQ' => 'Antarctica', 'AG' => 'Antigua and Barbuda', 'AR' => 'Argentina', 'AM' => 'Armenia', 'AW' => 'Aruba', 'AU' => 'Australia', 'AT' => 'Austria', 'AZ' => 'Azerbaijan', 'BS' => 'Bahamas', 'BH' => 'Bahrain', 'BD' => 'Bangladesh', 'BB' => 'Barbados', 'BY' => 'Belarus', 'BE' => 'Belgium', 'BZ' => 'Belize', 'BJ' => 'Benin', 'BM' => 'Bermuda', 'BT' => 'Bhutan', 'BO' => 'Bolivia', 'BQ' => 'Bonaire, Saint Eustatius and Saba', 'BA' => 'Bosnia and Herzegovina', 'BW' => 'Botswana', 'BV' => 'Bouvet Island', 'BR' => 'Brazil', 'IO' => 'British Indian Ocean Territory', 'VG' => 'British Virgin Islands', 'BN' => 'Brunei', 'BG' => 'Bulgaria', 'BF' => 'Burkina Faso', 'BI' => 'Burundi', 'KH' => 'Cambodia', 'CM' => 'Cameroon', 'CA' => 'Canada', 'CV' => 'Cape Verde', 'KY' => 'Cayman Islands', 'CF' => 'Central African Republic', 'TD' => 'Chad', 'CL' => 'Chile', 'CN' => 'China', 'CX' => 'Christmas Island', 'CC' => 'Cocos Islands', 'CO' => 'Colombia', 'KM' => 'Comoros', 'CK' => 'Cook Islands', 'CR' => 'Costa Rica', 'HR' => 'Croatia', 'CU' => 'Cuba', 'CW' => 'Curacao', 'CY' => 'Cyprus', 'CZ' => 'Czech Republic', 'CD' => 'Democratic Republic of the Congo', 'DK' => 'Denmark', 'DJ' => 'Djibouti', 'DM' => 'Dominica', 'DO' => 'Dominican Republic', 'TL' => 'East Timor', 'EC' => 'Ecuador', 'EG' => 'Egypt', 'SV' => 'El Salvador', 'GQ' => 'Equatorial Guinea', 'ER' => 'Eritrea', 'EE' => 'Estonia', 'ET' => 'Ethiopia', 'FK' => 'Falkland Islands', 'FO' => 'Faroe Islands', 'FJ' => 'Fiji', 'FI' => 'Finland', 'FR' => 'France', 'GF' => 'French Guiana', 'PF' => 'French Polynesia', 'TF' => 'French Southern Territories', 'GA' => 'Gabon', 'GM' => 'Gambia', 'GE' => 'Georgia', 'DE' => 'Germany', 'GH' => 'Ghana', 'GI' => 'Gibraltar', 'GR' => 'Greece', 'GL' => 'Greenland', 'GD' => 'Grenada', 'GP' => 'Guadeloupe', 'GU' => 'Guam', 'GT' => 'Guatemala', 'GG' => 'Guernsey', 'GN' => 'Guinea', 'GW' => 'Guinea-Bissau', 'GY' => 'Guyana', 'HT' => 'Haiti', 'HM' => 'Heard Island and McDonald Islands', 'HN' => 'Honduras', 'HK' => 'Hong Kong', 'HU' => 'Hungary', 'IS' => 'Iceland', 'IN' => 'India', 'ID' => 'Indonesia', 'IR' => 'Iran', 'IQ' => 'Iraq', 'IE' => 'Ireland', 'IM' => 'Isle of Man', 'IL' => 'Israel', 'IT' => 'Italy', 'CI' => 'Ivory Coast', 'JM' => 'Jamaica', 'JP' => 'Japan', 'JE' => 'Jersey', 'JO' => 'Jordan', 'KZ' => 'Kazakhstan', 'KE' => 'Kenya', 'KI' => 'Kiribati', 'XK' => 'Kosovo', 'KW' => 'Kuwait', 'KG' => 'Kyrgyzstan', 'LA' => 'Laos', 'LV' => 'Latvia', 'LB' => 'Lebanon', 'LS' => 'Lesotho', 'LR' => 'Liberia', 'LY' => 'Libya', 'LI' => 'Liechtenstein', 'LT' => 'Lithuania', 'LU' => 'Luxembourg', 'MO' => 'Macao', 'MK' => 'Macedonia', 'MG' => 'Madagascar', 'MW' => 'Malawi', 'MY' => 'Malaysia', 'MV' => 'Maldives', 'ML' => 'Mali', 'MT' => 'Malta', 'MH' => 'Marshall Islands', 'MQ' => 'Martinique', 'MR' => 'Mauritania', 'MU' => 'Mauritius', 'YT' => 'Mayotte', 'MX' => 'Mexico', 'FM' => 'Micronesia', 'MD' => 'Moldova', 'MC' => 'Monaco', 'MN' => 'Mongolia', 'ME' => 'Montenegro', 'MS' => 'Montserrat', 'MA' => 'Morocco', 'MZ' => 'Mozambique', 'MM' => 'Myanmar', 'NA' => 'Namibia', 'NR' => 'Nauru', 'NP' => 'Nepal', 'NL' => 'Netherlands', 'NC' => 'New Caledonia', 'NZ' => 'New Zealand', 'NI' => 'Nicaragua', 'NE' => 'Niger', 'NG' => 'Nigeria', 'NU' => 'Niue', 'NF' => 'Norfolk Island', 'KP' => 'North Korea', 'MP' => 'Northern Mariana Islands', 'NO' => 'Norway', 'OM' => 'Oman', 'PK' => 'Pakistan', 'PW' => 'Palau', 'PS' => 'Palestinian Territory', 'PA' => 'Panama', 'PG' => 'Papua New Guinea', 'PY' => 'Paraguay', 'PE' => 'Peru', 'PH' => 'Philippines', 'PN' => 'Pitcairn', 'PL' => 'Poland', 'PT' => 'Portugal', 'PR' => 'Puerto Rico', 'QA' => 'Qatar', 'CG' => 'Republic of the Congo', 'RE' => 'Reunion', 'RO' => 'Romania', 'RU' => 'Russia', 'RW' => 'Rwanda', 'BL' => 'Saint Barthelemy', 'SH' => 'Saint Helena', 'KN' => 'Saint Kitts and Nevis', 'LC' => 'Saint Lucia', 'MF' => 'Saint Martin', 'PM' => 'Saint Pierre and Miquelon', 'VC' => 'Saint Vincent and the Grenadines', 'WS' => 'Samoa', 'SM' => 'San Marino', 'ST' => 'Sao Tome and Principe', 'SA' => 'Saudi Arabia', 'SN' => 'Senegal', 'RS' => 'Serbia', 'SC' => 'Seychelles', 'SL' => 'Sierra Leone', 'SG' => 'Singapore', 'SX' => 'Sint Maarten', 'SK' => 'Slovakia', 'SI' => 'Slovenia', 'SB' => 'Solomon Islands', 'SO' => 'Somalia', 'ZA' => 'South Africa', 'GS' => 'South Georgia and the South Sandwich Islands', 'KR' => 'South Korea', 'SS' => 'South Sudan', 'ES' => 'Spain', 'LK' => 'Sri Lanka', 'SD' => 'Sudan', 'SR' => 'Suriname', 'SJ' => 'Svalbard and Jan Mayen', 'SZ' => 'Swaziland', 'SE' => 'Sweden', 'CH' => 'Switzerland', 'SY' => 'Syria', 'TW' => 'Taiwan', 'TJ' => 'Tajikistan', 'TZ' => 'Tanzania', 'TH' => 'Thailand', 'TG' => 'Togo', 'TK' => 'Tokelau', 'TO' => 'Tonga', 'TT' => 'Trinidad and Tobago', 'TN' => 'Tunisia', 'TR' => 'Turkey', 'TM' => 'Turkmenistan', 'TC' => 'Turks and Caicos Islands', 'TV' => 'Tuvalu', 'VI' => 'U.S. Virgin Islands', 'UG' => 'Uganda', 'UA' => 'Ukraine', 'AE' => 'United Arab Emirates', 'GB' => 'United Kingdom', 'US' => 'United States', 'UM' => 'United States Minor Outlying Islands', 'UY' => 'Uruguay', 'UZ' => 'Uzbekistan', 'VU' => 'Vanuatu', 'VA' => 'Vatican', 'VE' => 'Venezuela', 'VN' => 'Vietnam', 'WF' => 'Wallis and Futuna', 'EH' => 'Western Sahara', 'YE' => 'Yemen', 'ZM' => 'Zambia', 'ZW' => 'Zimbabwe', ) ?>
    freemius/templates/checkout/redirect.php000064400000005624147600046700014513 0ustar00get_id(); } $plan_id = fs_request_get( 'plan_id' ); $licenses = fs_request_get( 'licenses' ); $query_params = $fs_checkout->get_query_params( $fs, $plugin_id, $plan_id, $licenses ); // The return URL is a special page which will process the result. $return_url = $fs_checkout->get_checkout_redirect_return_url( $fs ); $query_params['return_url'] = $return_url; // Add the cancel URL to the same pricing page the request originated from. $query_params['cancel_url'] = $fs->pricing_url( fs_request_get( 'billing_cycle', 'annual' ), fs_request_get_bool( 'trial' ) ); if ( has_site_icon() ) { $query_params['cancel_icon'] = get_site_icon_url(); } // If the user didn't connect his account with Freemius, // once he accepts the Terms of Service and Privacy Policy, // and then click the purchase button, the context information // of the user will be shared with Freemius in order to complete the // purchase workflow and activate the license for the right user. $install_data = array_merge( $fs->get_opt_in_params(), array( 'activation_url' => fs_nonce_url( $fs->_get_admin_page_url( '', array( 'fs_action' => $fs->get_unique_affix() . '_activate_new', 'plugin_id' => $plugin_id, ) ), $fs->get_unique_affix() . '_activate_new' ), ) ); $query_params['install_data'] = json_encode( $install_data ); $query_params['_fs_dashboard_independent'] = true; $redirect_url = $fs_checkout->get_full_checkout_url( $query_params ); if ( ! fs_redirect( $redirect_url ) ) { // The Header was sent, so the server redirect failed. Rely on JS instead. ?>

    click here if you\'re stuck...' ), esc_url( $redirect_url ) ), array( 'a' => array( 'href' => true ) ) ); ?>

    get_id(); } $fs_checkout->verify_checkout_redirect_nonce( $fs ); wp_enqueue_script( 'jquery' ); wp_enqueue_script( 'json2' ); fs_enqueue_local_script( 'fs-form', 'jquery.form.js', array( 'jquery' ) ); $action = fs_request_get( '_fs_checkout_action' ); $data = json_decode( fs_request_get_raw( '_fs_checkout_data' ) ); ?>

    freemius/templates/checkout/frame.php000064400000014624147600046700014004 0ustar00get_slug(); $fs_checkout = FS_Checkout_Manager::instance(); $plugin_id = fs_request_get( 'plugin_id' ); if ( ! FS_Plugin::is_valid_id( $plugin_id ) ) { $plugin_id = $fs->get_id(); } $plan_id = fs_request_get( 'plan_id' ); $licenses = fs_request_get( 'licenses' ); $query_params = $fs_checkout->get_query_params( $fs, $plugin_id, $plan_id, $licenses ); $return_url = $fs->_get_sync_license_url( $plugin_id ); $query_params['return_url'] = $return_url; $xdebug_session = fs_request_get( 'XDEBUG_SESSION' ); if ( false !== $xdebug_session ) { $query_params['XDEBUG_SESSION'] = $xdebug_session; } $view_params = array( 'id' => $VARS['id'], 'page' => strtolower( $fs->get_text_inline( 'Checkout', 'checkout' ) ) . ' ' . $fs->get_text_inline( 'PCI compliant', 'pci-compliant' ), ); fs_require_once_template('secure-https-header.php', $view_params); ?>
    freemius/templates/connect/permissions-group.php000064400000004767147600046700016252 0ustar00get_text_x_inline( 'Opt Out', 'verb', 'opt-out' ); $opt_in_text = $fs->get_text_x_inline( 'Opt In', 'verb', 'opt-in' ); if ( empty( $permission_group[ 'prompt' ] ) ) { $is_enabled = false; foreach ( $permission_group[ 'permissions' ] as $permission ) { if ( true === $permission[ 'default' ] ) { // Even if one of the permissions is on, treat as if the entire group is on. $is_enabled = true; break; } } } else { $is_enabled = ( isset( $permission_group['is_enabled'] ) && true === $permission_group['is_enabled'] ); } ?>

      render_permission( $permission ); } ?>
    freemius/templates/connect/permission.php000064400000003407147600046700014723 0ustar00
  • class="fs-tooltip-trigger">

  • freemius/templates/connect/index.php000064400000000127147600046700013636 0ustar00get_option( $module_type . 's' ), FS_Plugin::get_class_name() ); if ( is_array( $modules ) && count( $modules ) > 0 ) { foreach ( $modules as $slug => $data ) { if ( WP_FS__MODULE_TYPE_THEME === $module_type ) { $current_theme = wp_get_theme(); $is_active = ( $current_theme->stylesheet === $data->file ); } else { $is_active = is_plugin_active( $data->file ); } /** * @author Vova Feldman * * @since 1.2.1 Don't load data from inactive modules. */ if ( $is_active ) { $fs = freemius( $data->id ); $next_execution = $fs->next_sync_cron(); $last_execution = $fs->last_sync_cron(); if ( false !== $next_execution ) { $scheduled_crons[ $slug ][] = array( 'name' => $fs->get_plugin_name(), 'slug' => $slug, 'module_type' => $fs->get_module_type(), 'type' => 'sync_cron', 'last' => $last_execution, 'next' => $next_execution, ); } $next_install_execution = $fs->next_install_sync(); $last_install_execution = $fs->last_install_sync(); if (false !== $next_install_execution || false !== $last_install_execution ) { $scheduled_crons[ $slug ][] = array( 'name' => $fs->get_plugin_name(), 'slug' => $slug, 'module_type' => $fs->get_module_type(), 'type' => 'install_sync', 'last' => $last_install_execution, 'next' => $next_install_execution, ); } } } } } $sec_text = fs_text_x_inline( 'sec', 'seconds' ); ?>

    $crons ) : ?>
    freemius/templates/debug/plugins-themes-sync.php000064400000005300147600046700016100 0ustar00get_option( 'all_plugins' ); $all_themes = $fs_options->get_option( 'all_themes' ); /* translators: %s: time period (e.g. In "2 hours") */ $in_x_text = fs_text_inline( 'In %s', 'in-x' ); /* translators: %s: time period (e.g. "2 hours" ago) */ $x_ago_text = fs_text_inline( '%s ago', 'x-ago' ); $sec_text = fs_text_x_inline( 'sec', 'seconds' ); ?>

    plugins ) ?> timestamp ) && is_numeric( $all_plugins->timestamp ) ) { $diff = abs( WP_FS__SCRIPT_START_TIME - $all_plugins->timestamp ); $human_diff = ( $diff < MINUTE_IN_SECONDS ) ? $diff . ' ' . $sec_text : human_time_diff( WP_FS__SCRIPT_START_TIME, $all_plugins->timestamp ); echo esc_html( sprintf( ( ( WP_FS__SCRIPT_START_TIME < $all_plugins->timestamp ) ? $in_x_text : $x_ago_text ), $human_diff ) ); } ?>
    themes ) ?> timestamp ) && is_numeric( $all_themes->timestamp ) ) { $diff = abs( WP_FS__SCRIPT_START_TIME - $all_themes->timestamp ); $human_diff = ( $diff < MINUTE_IN_SECONDS ) ? $diff . ' ' . $sec_text : human_time_diff( WP_FS__SCRIPT_START_TIME, $all_themes->timestamp ); echo esc_html( sprintf( ( ( WP_FS__SCRIPT_START_TIME < $all_themes->timestamp ) ? $in_x_text : $x_ago_text ), $human_diff ) ); } ?>
    freemius/templates/debug/logger.php000064400000004015147600046700013443 0ustar00

    >
    #
    . get_id() ?> %s', esc_html( substr( $log['msg'], 0, 32 ) ) . ( 32 < strlen( $log['msg'] ) ? '...' : '' ) ); ?>
    get_file() ) . ':' . $log['line']; } ?>
    freemius/templates/debug/index.php000064400000000127147600046700013273 0ustar00 0, 'POST' => 0, 'PUT' => 0, 'DELETE' => 0 ); $show_body = false; foreach ( $logger as $log ) { $counters[ $log['method'] ] ++; if ( ! is_null( $log['body'] ) ) { $show_body = true; } } $pretty_print = $show_body && defined( 'JSON_PRETTY_PRINT' ) && version_compare( phpversion(), '5.3', '>=' ); /** * This template is used for debugging, therefore, when possible * we'd like to prettify the output of a JSON encoded variable. * This will only be executed when $pretty_print is `true`, and * the var is `true` only for PHP 5.3 and higher. Due to the * limitations of the current Theme Check, it throws an error * that using the "options" parameter (the 2nd param) is not * supported in PHP 5.2 and lower. Thus, we added this alias * variable to work around that false-positive. * * @author Vova Feldman (@svovaf) * @since 1.2.2.7 */ $encode = 'json_encode'; $root_path_len = strlen( ABSPATH ); $ms_text = fs_text_x_inline( 'ms', 'milliseconds' ); ?>

    Total Time:

    Total Requests:

    $count ) : ?>

    :

    #
    . %s', $log['path'] ); ?> %s', substr( $body, 0, 32 ) . ( 32 < strlen( $body ) ? '...' : '' ) ); if ( $pretty_print ) { $body = $encode( json_decode( $log['body'] ), JSON_PRETTY_PRINT ); } ?>
    %s', substr( $result, 0, 32 ) . ( 32 < strlen( $result ) ? '...' : '' ) ); } if ( $is_not_empty_result && $pretty_print ) { $decoded = json_decode( $result ); if ( ! is_null( $decoded ) ) { $result = $encode( $decoded, JSON_PRETTY_PRINT ); } } else { $result = is_string( $result ) ? $result : json_encode( $result ); } ?> style="display: none">
    freemius/templates/forms/deactivation/retry-skip.php000064400000002222147600046700017005 0ustar00get_slug(); $skip_url = fs_nonce_url( $fs->_get_admin_page_url( '', array( 'fs_action' => $fs->get_unique_affix() . '_skip_activation' ) ), $fs->get_unique_affix() . '_skip_activation' ); $skip_text = strtolower( fs_text_x_inline( 'Skip', 'verb', 'skip', $slug ) ); $use_plugin_anonymously_text = fs_text_inline( 'Click here to use the plugin anonymously', 'click-here-to-use-plugin-anonymously', $slug ); echo sprintf( fs_text_inline( "You might have missed it, but you don't have to share any data and can just %s the opt-in.", 'dont-have-to-share-any-data', $slug ), "{$skip_text}" ) . " {$use_plugin_anonymously_text}";freemius/templates/forms/deactivation/index.php000064400000000127147600046700016005 0ustar00get_slug(); $subscription_cancellation_dialog_box_template_params = $VARS['subscription_cancellation_dialog_box_template_params']; $show_deactivation_feedback_form = $VARS['show_deactivation_feedback_form']; $confirmation_message = $VARS['uninstall_confirmation_message']; $is_anonymous = ( ! $fs->is_registered() ); $anonymous_feedback_checkbox_html = ''; $reasons_list_items_html = ''; $snooze_select_html = ''; if ( $show_deactivation_feedback_form ) { $reasons = $VARS['reasons']; foreach ( $reasons as $reason ) { $list_item_classes = 'reason' . ( ! empty( $reason['input_type'] ) ? ' has-input' : '' ); if ( isset( $reason['internal_message'] ) && ! empty( $reason['internal_message'] ) ) { $list_item_classes .= ' has-internal-message'; $reason_internal_message = $reason['internal_message']; } else { $reason_internal_message = ''; } $reason_input_type = ( ! empty( $reason['input_type'] ) ? $reason['input_type'] : '' ); $reason_input_placeholder = ( ! empty( $reason['input_placeholder'] ) ? $reason['input_placeholder'] : '' ); $reason_list_item_html = <<< HTML
  • {$reason_internal_message}
  • HTML; $reasons_list_items_html .= $reason_list_item_html; } if ( $is_anonymous ) { $anonymous_feedback_checkbox_html = sprintf( '', fs_esc_html_inline( 'Anonymous feedback', 'anonymous-feedback', $slug ) ); } $snooze_periods = array( array( 'increment' => fs_text_inline( 'hour', $slug ), 'quantity' => number_format_i18n(1), 'value' => 6 * WP_FS__TIME_10_MIN_IN_SEC, ), array( 'increment' => fs_text_inline( 'hours', $slug ), 'quantity' => number_format_i18n(24), 'value' => WP_FS__TIME_24_HOURS_IN_SEC, ), array( 'increment' => fs_text_inline( 'days', $slug ), 'quantity' => number_format_i18n(7), 'value' => WP_FS__TIME_WEEK_IN_SEC, ), array( 'increment' => fs_text_inline( 'days', $slug ), 'quantity' => number_format_i18n(30), 'value' => 30 * WP_FS__TIME_24_HOURS_IN_SEC, ), ); $snooze_select_html = ''; } // Aliases. $deactivate_text = fs_text_inline( 'Deactivate', 'deactivate', $slug ); $theme_text = fs_text_inline( 'Theme', 'theme', $slug ); $activate_x_text = fs_text_inline( 'Activate %s', 'activate-x', $slug ); $submit_deactivate_text = sprintf( fs_text_inline( 'Submit & %s', 'deactivation-modal-button-submit', $slug ), $fs->is_plugin() ? $deactivate_text : sprintf( $activate_x_text, $theme_text ) ); fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); if ( ! empty( $subscription_cancellation_dialog_box_template_params ) ) { fs_require_template( 'forms/subscription-cancellation.php', $subscription_cancellation_dialog_box_template_params ); } ?> freemius/templates/forms/deactivation/contact.php000064400000001423147600046700016331 0ustar00get_slug(); echo fs_text_inline( 'Sorry for the inconvenience and we are here to help if you give us a chance.', 'contact-support-before-deactivation', $slug ) . sprintf(" %s", $fs->contact_url( 'technical_support' ), fs_text_inline( 'Contact Support', 'contact-support', $slug ) ); freemius/templates/forms/user-change.php000064400000026712147600046700014435 0ustar00get_slug(); /** * @var object[] $license_owners */ $license_owners = $VARS['license_owners']; $change_user_message = fs_text_inline( 'By changing the user, you agree to transfer the account ownership to:', 'change-user--message', $slug ); $header_title = fs_text_inline( 'Change User', 'change-user', $slug ); $user_change_button_text = fs_text_inline( 'I Agree - Change User', 'agree-change-user', $slug ); $other_text = fs_text_inline( 'Other', 'other', $slug ); $enter_email_address_placeholder_text = fs_text_inline( 'Enter email address', 'enter-email-address', $slug ); $user_change_options_html = <<< HTML
    HTML; foreach ( $license_owners as $license_owner ) { $user_change_options_html .= <<< HTML HTML; } $user_change_options_html .= <<< HTML
    HTML; $modal_content_html = <<< HTML

    {$change_user_message}

    {$user_change_options_html} HTML; fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); ?> freemius/templates/forms/trial-start.php000064400000012353147600046700014476 0ustar00get_slug(); $message_header = sprintf( /* translators: %1$s: Number of trial days; %2$s: Plan name; */ fs_text_inline( 'You are 1-click away from starting your %1$s-day free trial of the %2$s plan.', 'start-trial-prompt-header', $slug ), '', '' ); $message_content = sprintf( /* translators: %s: Link to freemius.com */ fs_text_inline( 'For compliance with the WordPress.org guidelines, before we start the trial we ask that you opt in with your user and non-sensitive site information, allowing the %s to periodically send data to %s to check for version updates and to validate your trial.', 'start-trial-prompt-message', $slug ), $fs->get_module_type(), sprintf( '%s', 'https://freemius.com', 'freemius.com' ) ); $modal_content_html = <<< HTML

    {$message_header}

    {$message_content}

    HTML; fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); ?> freemius/templates/forms/subscription-cancellation.php000064400000026753147600046700017417 0ustar00get_slug(); /** * @var FS_Plugin_License $license */ $license = $VARS['license']; $has_trial = $VARS['has_trial']; $subscription_cancellation_context = $has_trial ? fs_text_inline( 'trial', 'trial', $slug ) : fs_text_inline( 'subscription', 'subscription', $slug ); $plan = $fs->get_plan(); $module_label = $fs->get_module_label( true ); if ( $VARS['is_license_deactivation'] ) { $subscription_cancellation_text = ''; } else { $subscription_cancellation_text = sprintf( fs_text_inline( "Deactivating or uninstalling the %s will automatically disable the license, which you'll be able to use on another site.", 'deactivation-or-uninstall-message', $slug ), $module_label ) . ' '; } $subscription_cancellation_text .= sprintf( fs_text_inline( 'In case you are NOT planning on using this %s on this site (or any other site) - would you like to cancel the %s as well?', 'cancel-subscription-message', $slug ), ( $VARS['is_license_deactivation'] ? fs_text_inline( 'license', 'license', $slug ) : $module_label ), $subscription_cancellation_context ); $cancel_subscription_action_label = sprintf( fs_esc_html_inline( "Cancel %s - I no longer need any security & feature updates, nor support for %s because I'm not planning to use the %s on this, or any other site.", 'cancel-x', $slug ), esc_html( $subscription_cancellation_context ), sprintf( '%s', esc_html( $fs->get_plugin_title() ) ), esc_html( $module_label ) ); $keep_subscription_active_action_label = esc_html( sprintf( fs_text_inline( "Don't cancel %s - I'm still interested in getting security & feature updates, as well as be able to contact support.", 'dont-cancel-x', $slug ), $subscription_cancellation_context ) ); $subscription_cancellation_text = esc_html( $subscription_cancellation_text ); $subscription_cancellation_html = <<< HTML

    {$subscription_cancellation_text}

    HTML; $downgrading_plan_text = fs_text_inline( 'Downgrading your plan', 'downgrading-plan', $slug ); $cancelling_subscription_text = fs_text_inline( 'Cancelling the subscription', 'cancelling-subscription', $slug ); /* translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription' */ $downgrade_x_confirm_text = fs_text_inline( '%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.', 'downgrade-x-confirm', $slug ); $prices_increase_text = fs_text_inline( 'Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.', 'pricing-increase-warning', $slug ); $after_downgrade_non_blocking_text = fs_text_inline( 'You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.', 'after-downgrade-non-blocking', $slug ); $after_downgrade_blocking_text = fs_text_inline( 'Once your license expires you can still use the Free version but you will NOT have access to the %s features.', 'after-downgrade-blocking', $slug ); $after_downgrade_blocking_text_premium_only = fs_text_inline( 'Once your license expires you will no longer be able to use the %s, unless you activate it again with a valid premium license.', 'after-downgrade-blocking-premium-only', $slug ); $subscription_cancellation_confirmation_message = $has_trial ? fs_text_inline( 'Cancelling the trial will immediately block access to all premium features. Are you sure?', 'cancel-trial-confirm', $slug ) : sprintf( '%s %s %s %s', sprintf( $downgrade_x_confirm_text, ($fs->is_only_premium() ? $cancelling_subscription_text : $downgrading_plan_text ), $plan->title, human_time_diff( time(), strtotime( $license->expiration ) ) ), ( $license->is_block_features ? ( $fs->is_only_premium() ? sprintf( $after_downgrade_blocking_text_premium_only, $module_label ) : sprintf( $after_downgrade_blocking_text, $plan->title ) ) : sprintf( $after_downgrade_non_blocking_text, $plan->title, $fs->get_module_label( true ) ) ), $prices_increase_text, fs_esc_attr_inline( 'Are you sure you want to proceed?', 'proceed-confirmation', $slug ) ); fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); ?> freemius/templates/forms/resend-key.php000064400000016271147600046700014301 0ustar00get_slug(); $send_button_text = fs_text_inline( 'Send License Key', 'send-license-key', $slug ); $cancel_button_text = fs_text_inline( 'Cancel', 'cancel', $slug ); $email_address_placeholder = fs_esc_attr_inline( 'Email address', 'email-address', $slug ); $other_text = fs_text_inline( 'Other', 'other', $slug ); $is_freemium = $fs->is_freemium(); $send_button_text_html = esc_html($send_button_text); $button_html = <<< HTML HTML; if ( $is_freemium ) { $current_user = Freemius::_get_current_wp_user(); $email = $current_user->user_email; $esc_email = esc_attr( $email ); $form_html = <<< HTML {$button_html} HTML; } else { $email = ''; $form_html = <<< HTML {$button_html} HTML; } $message_above_input_field = $fs->is_only_premium() ? fs_esc_html_inline( "Enter the email address you've used during the purchase and we will resend you the license key.", 'ask-for-upgrade-email-address-premium-only', $slug ) : fs_esc_html_inline( "Enter the email address you've used for the upgrade below and we will resend you the license key.", 'ask-for-upgrade-email-address', $slug ); $modal_content_html = <<< HTML

    {$message_above_input_field}

    {$form_html}
    HTML; fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); ?> freemius/templates/forms/premium-versions-upgrade-metadata.php000064400000002770147600046700020761 0ustar00_get_license(); if ( ! is_object( $license ) ) { $purchase_url = $fs->pricing_url(); } else { $subscription = $fs->_get_subscription( $license->id ); $purchase_url = $fs->checkout_url( is_object( $subscription ) ? ( 1 == $subscription->billing_cycle ? WP_FS__PERIOD_MONTHLY : WP_FS__PERIOD_ANNUALLY ) : WP_FS__PERIOD_LIFETIME, false, array( 'licenses' => $license->quota ) ); } $plugin_data = $fs->get_plugin_data(); ?> freemius/templates/forms/premium-versions-upgrade-handler.php000064400000020213147600046700020606 0ustar00get_slug(); $plugin_data = $fs->get_plugin_data(); $plugin_name = $plugin_data['Name']; $plugin_basename = $fs->get_plugin_basename(); $license = $fs->_get_license(); if ( ! is_object( $license ) ) { $purchase_url = $fs->pricing_url(); } else { $subscription = $fs->_get_subscription( $license->id ); $purchase_url = $fs->checkout_url( is_object( $subscription ) ? ( 1 == $subscription->billing_cycle ? WP_FS__PERIOD_MONTHLY : WP_FS__PERIOD_ANNUALLY ) : WP_FS__PERIOD_LIFETIME, false, array( 'licenses' => $license->quota ) ); } $message = sprintf( fs_text_inline( 'There is a new version of %s available.', 'new-version-available-message', $slug ) . fs_text_inline( ' %s to access version %s security & feature updates, and support.', 'x-for-updates-and-support', $slug ), '', sprintf( '%s', is_object( $license ) ? fs_text_inline( 'Renew your license now', 'renew-license-now', $slug ) : fs_text_inline( 'Buy a license now', 'buy-license-now', $slug ) ), '' ); $modal_content_html = "

    {$message}

    "; $header_title = fs_text_inline( 'New Version Available', 'new-version-available', $slug ); $renew_license_button_text = is_object( $license ) ? fs_text_inline( 'Renew license', 'renew-license', $slug ) : fs_text_inline( 'Buy license', 'buy-license', $slug ); fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); ?> freemius/templates/forms/optout.php000064400000023235147600046700013563 0ustar00get_slug(); $reconnect_url = $fs->get_activation_url( array( 'nonce' => wp_create_nonce( $fs->get_unique_affix() . '_reconnect' ), 'fs_action' => ( $fs->get_unique_affix() . '_reconnect' ), ) ); $plugin_title = "" . esc_html( $fs->get_plugin()->title ) . ""; $opt_out_text = fs_text_x_inline( 'Opt Out', 'verb', 'opt-out', $slug ); $permission_manager = FS_Permission_Manager::instance( $fs ); fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); fs_enqueue_local_style( 'fs_optout', '/admin/optout.css' ); fs_enqueue_local_style( 'fs_common', '/admin/common.css' ); if ( ! $permission_manager->is_premium_context() ) { $optional_permissions = array( $permission_manager->get_extensions_permission( false, false, true ) ); $permission_groups = array( array( 'id' => 'communication', 'type' => 'required', 'title' => $fs->get_text_inline( 'Communication', 'communication' ), 'desc' => '', 'permissions' => $permission_manager->get_opt_in_required_permissions( true ), 'is_enabled' => $fs->is_registered(), 'prompt' => array( $fs->esc_html_inline( "Sharing your name and email allows us to keep you in the loop about new features and important updates, warn you about security issues before they become public knowledge, and send you special offers.", 'opt-out-message_user' ), sprintf( $fs->esc_html_inline( 'By clicking "Opt Out", %s will no longer be able to view your name and email.', 'opt-out-message-clicking-opt-out' ), $plugin_title ), ), 'prompt_cancel_label' => $fs->get_text_inline( 'Stay Connected', 'stay-connected' ) ), array( 'id' => 'diagnostic', 'type' => 'required', 'title' => $fs->get_text_inline( 'Diagnostic Info', 'diagnostic-info' ), 'desc' => '', 'permissions' => $permission_manager->get_opt_in_diagnostic_permissions( true ), 'is_enabled' => $fs->is_tracking_allowed(), 'prompt' => array( sprintf( $fs->esc_html_inline( 'Sharing diagnostic data helps to provide additional functionality that\'s relevant to your website, avoid WordPress or PHP version incompatibilities that can break the website, and recognize which languages & regions the %s should be translated and tailored to.', 'opt-out-message-clicking-opt-out' ), $fs->get_module_type() ), sprintf( $fs->esc_html_inline( 'By clicking "Opt Out", diagnostic data will no longer be sent to %s.', 'opt-out-message-clicking-opt-out' ), $plugin_title ), ), 'prompt_cancel_label' => $fs->get_text_inline( 'Keep Sharing', 'keep-sharing' ) ), array( 'id' => 'extensions', 'type' => 'optional', 'title' => $fs->get_text_inline( 'Extensions', 'extensions' ), 'desc' => '', 'permissions' => $optional_permissions, ), ); } else { $optional_permissions = $permission_manager->get_license_optional_permissions( false, true ); $permission_groups = array( array( 'id' => 'essentials', 'type' => 'required', 'title' => $fs->esc_html_inline( 'Required', 'required' ), 'desc' => sprintf( $fs->esc_html_inline( 'For automatic delivery of security & feature updates, and license management & protection, %s needs to:', 'license-sync-disclaimer' ), '' . esc_html( $fs->get_plugin_title() ) . '' ), 'permissions' => $permission_manager->get_license_required_permissions( true ), 'is_enabled' => $permission_manager->is_essentials_tracking_allowed(), 'prompt' => array( sprintf( $fs->esc_html_inline( 'To ensure that security & feature updates are automatically delivered directly to your WordPress Admin Dashboard while protecting your license from unauthorized abuse, %2$s needs to view the website’s homepage URL, %1$s version, SDK version, and whether the %1$s is active.', 'premium-opt-out-message-usage-tracking' ), $fs->get_module_type(), $plugin_title ), sprintf( $fs->esc_html_inline( 'By opting out from sharing this information with the updates server, you’ll have to check for new %1$s releases and manually download & install them. Not just a hassle, but missing an update can put your site at risk and cause undue compatibility issues, so we highly recommend keeping these essential permissions on.', 'opt-out-message-clicking-opt-out' ), $fs->get_module_type(), $plugin_title ), ), 'prompt_cancel_label' => $fs->get_text_inline( 'Keep automatic updates', 'premium-opt-out-cancel' ) ), array( 'id' => 'optional', 'type' => 'optional', 'title' => $fs->esc_html_inline( 'Optional', 'optional' ), 'desc' => sprintf( $fs->esc_html_inline( 'For ongoing compatibility with your website, you can optionally allow %s to:', 'optional-permissions-disclaimer' ), $plugin_title ), 'permissions' => $optional_permissions, ), ); } $ajax_action = 'toggle_permission_tracking'; $form_id = "fs_opt_out_{$fs->get_id()}"; ?> require_permissions_js( false ) ?> freemius/templates/forms/license-activation.php000064400000104417147600046700016014 0ustar00get_slug(); $unique_affix = $fs->get_unique_affix(); $cant_find_license_key_text = fs_text_inline( "Can't find your license key?", 'cant-find-license-key', $slug ); $message_above_input_field = fs_text_inline( 'Please enter the license key that you received in the email right after the purchase:', 'activate-license-message', $slug ); $message_below_input_field = ''; $header_title = $fs->is_free_plan() ? fs_text_inline( 'Activate License', 'activate-license', $slug ) : fs_text_inline( 'Update License', 'update-license', $slug ); if ( $fs->is_registered() ) { $activate_button_text = $header_title; } else { $message_below_input_field = sprintf( fs_text_inline( 'The %1$s will be periodically sending essential license data to %2$s to check for security and feature updates, and verify the validity of your license.', 'license-sync-disclaimer', $slug ), $fs->get_module_label( true ), "{$fs->get_plugin_title()}" ); $activate_button_text = fs_text_inline( 'Agree & Activate License', 'agree-activate-license', $slug ); } $license_key_text = fs_text_inline( 'License key', 'license-key' , $slug ); $is_network_activation = ( $fs->is_network_active() && fs_is_network_admin() && ! $fs->is_delegated_connection() ); $network_activation_html = ''; $sites_details = array(); if ( $is_network_activation ) { $all_sites = Freemius::get_sites(); $all_site_details = array(); $subsite_url_by_install_id = array(); $install_url_by_install_id = array(); foreach ( $all_sites as $site ) { $site_details = $fs->get_site_info( $site ); if ( FS_Clone_Manager::instance()->is_temporary_duplicate_by_blog_id( $site_details['blog_id'] ) ) { continue; } $blog_id = Freemius::get_site_blog_id( $site ); $install = $fs->get_install_by_blog_id($blog_id); if ( is_object( $install ) ) { if ( isset( $subsite_url_by_install_id[ $install->id ] ) ) { $clone_subsite_url = $subsite_url_by_install_id[ $install->id ]; $clone_install_url = $install_url_by_install_id[ $install->id ]; if ( /** * If we already have an install with the same URL as the subsite it's stored in, skip the current subsite. Otherwise, replace the existing install's data with the current subsite's install's data if the URLs match. * * @author Leo Fajardo (@leorw) * @since 2.5.0 */ fs_strip_url_protocol( untrailingslashit( $clone_install_url ) ) === fs_strip_url_protocol( untrailingslashit( $clone_subsite_url ) ) || fs_strip_url_protocol( untrailingslashit( $install->url ) ) !== fs_strip_url_protocol( untrailingslashit( $site_details['url'] ) ) ) { continue; } } if ( FS_Plugin_License::is_valid_id( $install->license_id ) ) { $site_details['license_id'] = $install->license_id; } $subsite_url_by_install_id[ $install->id ] = $site_details['url']; $install_url_by_install_id[ $install->id ] = $install->url; } $all_site_details[] = $site_details; } if ( $is_network_activation ) { $vars = array( 'id' => $fs->get_id(), 'sites' => $all_site_details, 'require_license_key' => true ); $network_activation_html = fs_get_template( 'partials/network-activation.php', $vars ); } } $premium_licenses = $fs->get_available_premium_licenses(); $available_licenses = array(); foreach ( $premium_licenses as $premium_license ) { $activations_left = $premium_license->left(); if ( ! ( $activations_left > 0 ) ) { continue; } $available_licenses[ $activations_left . '_' . $premium_license->id ] = $premium_license; } $total_available_licenses = count( $available_licenses ); if ( $total_available_licenses > 0 ) { $license_input_html = <<< HTML
    HTML; if ( $total_available_licenses > 1 ) { // Sort the licenses by number of activations left in descending order. krsort( $available_licenses ); $license_input_html .= ''; } else { $available_licenses = array_values( $available_licenses ); /** * @var FS_Plugin_License $available_license */ $available_license = $available_licenses[0]; $value = sprintf( "%s-Site %s License - %s", ( 1 == $available_license->quota ? 'Single' : ( $available_license->is_unlimited() ? 'Unlimited' : $available_license->quota ) ), $fs->_get_plan_by_id( $available_license->plan_id )->title, $available_license->get_html_escaped_masked_secret_key() ); $license_input_html .= <<< HTML HTML; } $license_input_html .= <<< HTML
    HTML; } else { $license_input_html = ""; } $ownership_change_option_text = fs_text_inline( "Associate with the license owner's account.", 'associate-account-with-license-owner', $slug ); $ownership_change_option_html = ""; /** * IMPORTANT: * DO NOT ADD MAXLENGTH OR LIMIT THE LICENSE KEY LENGTH SINCE * WE DO WANT TO ALLOW INPUT OF LONGER KEYS (E.G. WooCommerce Keys) * FOR MIGRATED MODULES. */ $modal_content_html = <<< HTML

    {$message_above_input_field}

    {$license_input_html} {$cant_find_license_key_text} {$network_activation_html}

    {$message_below_input_field}

    {$ownership_change_option_html} HTML; /** * Handle the ownership change option if not an add-on or if no license yet is activated for the * parent product in case of an add-on. * * @author Leo Fajardo (@leorw) * @since 2.3.2 */ $is_user_change_supported = ( ! $fs->is_addon() || ! $fs->get_parent_instance()->has_active_valid_license() ); fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); ?> get_slug(); $user = $fs->get_user(); $current_email_address = $user->email; fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); ?> freemius/templates/forms/data-debug-mode.php000064400000017062147600046700015151 0ustar00get_slug(); $unique_affix = $fs->get_unique_affix(); $last_license_user_id = $fs->get_last_license_user_id(); $has_last_license_user_id = FS_User::is_valid_id( $last_license_user_id ); $message_above_input_field = ( ! $has_last_license_user_id ) ? fs_text_inline( 'Please enter the license key to enable the debug mode:', 'submit-developer-license-key-message', $slug ) : sprintf( fs_text_inline( 'To enter the debug mode, please enter the secret key of the license owner (UserID = %d), which you can find in your "My Profile" section of your User Dashboard:', 'submit-addon-developer-key-message', $slug ), $last_license_user_id ); $processing_text = ( fs_esc_js_inline( 'Processing', 'processing', $slug ) . '...' ); $submit_button_text = fs_text_inline( 'Submit', 'submit', $slug ); $debug_license_link_text = fs_esc_html_inline( 'Start Debug', 'start-debug-license', $slug ); $license_or_user_key_text = ( ! $has_last_license_user_id ) ? fs_text_inline( 'License key', 'license-key' , $slug ) : fs_text_inline( 'User key', 'user-key' , $slug ); $input_html = ""; $modal_content_html = <<< HTML

    {$message_above_input_field}

    {$input_html} HTML; fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); ?> freemius/templates/forms/affiliation.php000064400000072002147600046700014512 0ustar00get_slug(); $user = $fs->get_user(); $affiliate = $fs->get_affiliate(); $affiliate_terms = $fs->get_affiliate_terms(); $module_type = $fs->is_plugin() ? WP_FS__MODULE_TYPE_PLUGIN : WP_FS__MODULE_TYPE_THEME; $commission = $affiliate_terms->get_formatted_commission(); $readonly = false; $is_affiliate = is_object( $affiliate ); $is_pending_affiliate = false; $email_address = ( is_object( $user ) ? $user->email : '' ); $full_name = ( is_object( $user ) ? $user->get_name() : '' ); $paypal_email_address = ''; $domain = ''; $extra_domains = array(); $promotion_method_social_media = false; $promotion_method_mobile_apps = false; $statistics_information = false; $promotion_method_description = false; $members_dashboard_login_url = 'https://users.freemius.com/login'; $affiliate_application_data = $fs->get_affiliate_application_data(); if ( $is_affiliate && $affiliate->is_pending() ) { $readonly = 'readonly'; $is_pending_affiliate = true; $paypal_email_address = $affiliate->paypal_email; $domain = $affiliate->domain; $statistics_information = $affiliate_application_data['stats_description']; $promotion_method_description = $affiliate_application_data['promotion_method_description']; if ( ! empty( $affiliate_application_data['additional_domains'] ) ) { $extra_domains = $affiliate_application_data['additional_domains']; } if ( ! empty( $affiliate_application_data['promotion_methods'] ) ) { $promotion_methods = explode( ',', $affiliate_application_data['promotion_methods'] ); $promotion_method_social_media = in_array( 'social_media', $promotion_methods ); $promotion_method_mobile_apps = in_array( 'mobile_apps', $promotion_methods ); } } else { if ( ! is_object( $user ) ) { $current_user = Freemius::_get_current_wp_user(); $full_name = trim( $current_user->user_firstname . ' ' . $current_user->user_lastname ); $email_address = $current_user->user_email; } $domain = Freemius::get_unfiltered_site_url( null, true ); } $affiliate_tracking = 30; if ( is_object( $affiliate_terms ) ) { $affiliate_tracking = ( ! is_null( $affiliate_terms->cookie_days ) ? ( $affiliate_terms->cookie_days . '-day' ) : fs_text_inline( 'Non-expiring', 'non-expiring', $slug ) ); } $apply_to_become_affiliate_text = fs_text_inline( 'Apply to become an affiliate', 'apply-to-become-an-affiliate', $slug ); $module_id = $fs->get_id(); $affiliate_program_terms_url = "https://freemius.com/plugin/{$module_id}/{$slug}/legal/affiliate-program/"; $has_tabs = $fs->_add_tabs_before_content(); ?>
    is_active() ) : ?>

    %s', $members_dashboard_login_url, $members_dashboard_login_url ) ); ?>

    is_suspended() ) { $message_text = fs_text_inline( 'Your affiliation account was temporarily suspended.', 'affiliate-account-suspended', $slug ); $message_container_class = 'notice notice-warning'; } else if ( $affiliate->is_rejected() ) { $message_text = fs_text_inline( "Thank you for applying for our affiliate program, unfortunately, we've decided at this point to reject your application. Please try again in 30 days.", 'affiliate-application-rejected', $slug ); $message_container_class = 'error'; } else if ( $affiliate->is_blocked() ) { $message_text = fs_text_inline( 'Due to violation of our affiliation terms, we decided to temporarily block your affiliation account. If you have any questions, please contact support.', 'affiliate-account-blocked', $slug ); $message_container_class = 'error'; } ?>

    • has_renewals_commission() ) : ?>
    • is_session_cookie() ) ) : ?>
    • has_lifetime_commission() ) : ?>
    >

    >
    >
    >
    >

    + ...
    >

    >
    />
    />

    _add_tabs_after_content(); } freemius/templates/js/style-premium-theme.php000064400000003157147600046700015434 0ustar00get_slug(); ?> freemius/templates/js/permissions.php000064400000051074147600046700014074 0ustar00 freemius/templates/js/open-license-activation.php000064400000002176147600046700016240 0ustar00 freemius/templates/js/jquery.content-change.php000064400000003050147600046700015723 0ustar00 freemius/templates/js/index.php000064400000000127147600046700012621 0ustar00get_slug(); $sites = $VARS['sites']; $require_license_key = $VARS['require_license_key']; $show_delegation_option = $fs->apply_filters( 'show_delegation_option', true ); $enable_per_site_activation = $fs->apply_filters( 'enable_per_site_activation', true ); ?> |' ?> freemius/templates/partials/index.php000064400000000032147600046700014017 0ustar00
      $url ) : ?>
    freemius/templates/plugin-info/index.php000064400000000127147600046700014434 0ustar00features) && is_array($plan->features)) { foreach ( $plan->features as $feature ) { if ( ! isset( $features_plan_map[ $feature->id ] ) ) { $features_plan_map[ $feature->id ] = array( 'feature' => $feature, 'plans' => array() ); } $features_plan_map[ $feature->id ]['plans'][ $plan->id ] = $feature; } } // Add support as a feature. if ( ! empty( $plan->support_email ) || ! empty( $plan->support_skype ) || ! empty( $plan->support_phone ) || true === $plan->is_success_manager ) { if ( ! isset( $features_plan_map['support'] ) ) { $support_feature = new stdClass(); $support_feature->id = 'support'; $support_feature->title = fs_text_inline( 'Support', $plugin->slug ); $features_plan_map[ $support_feature->id ] = array( 'feature' => $support_feature, 'plans' => array() ); } else { $support_feature = $features_plan_map['support']['feature']; } $features_plan_map[ $support_feature->id ]['plans'][ $plan->id ] = $support_feature; } } // Add updates as a feature for all plans. $updates_feature = new stdClass(); $updates_feature->id = 'updates'; $updates_feature->title = fs_text_inline( 'Unlimited Updates', 'unlimited-updates', $plugin->slug ); $features_plan_map[ $updates_feature->id ] = array( 'feature' => $updates_feature, 'plans' => array() ); foreach ( $plans as $plan ) { $features_plan_map[ $updates_feature->id ]['plans'][ $plan->id ] = $updates_feature; } ?>
    $data ) : ?>
    title ?> pricing ) ) { fs_esc_html_echo_inline( 'Free', 'free', $plugin->slug ); } else { foreach ( $plan->pricing as $pricing ) { /** * @var FS_Pricing $pricing */ if ( 1 == $pricing->licenses ) { if ( $pricing->has_annual() ) { echo "\${$pricing->annual_price} / " . fs_esc_html_x_inline( 'year', 'as annual period', 'year', $plugin->slug ); } else if ( $pricing->has_monthly() ) { echo "\${$pricing->monthly_price} / " . fs_esc_html_x_inline( 'mo', 'as monthly period', 'mo', $plugin->slug ); } else { echo "\${$pricing->lifetime_price}"; } } } } ?>
    title ) ) ?> id ] ) ) : ?> id ]->value ) ) : ?> id ]->value ) ?>
    freemius/templates/plugin-info/description.php000064400000004210147600046700015645 0ustar00info->selling_point_0 ) || ! empty( $plugin->info->selling_point_1 ) || ! empty( $plugin->info->selling_point_2 ) ) : ?>
      info->{'selling_point_' . $i} ) ) : ?>
    • info->{'selling_point_' . $i} ) ?>

    info->description, array( 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ), 'b' => array(), 'i' => array(), 'p' => array(), 'blockquote' => array(), 'h2' => array(), 'h3' => array(), 'ul' => array(), 'ol' => array(), 'li' => array() ) ); ?>
    info->screenshots ) ) : ?> info->screenshots ?>

    slug ) ?>

      $url ) : ?>
    freemius/templates/tabs.php000064400000015204147600046700012031 0ustar00get_slug(); $menu_items = $fs->get_menu_items(); $show_settings_with_tabs = $fs->show_settings_with_tabs(); $tabs = array(); foreach ( $menu_items as $priority => $items ) { foreach ( $items as $item ) { if ( ! $item['show_submenu'] ) { $submenu_name = ('wp-support-forum' === $item['menu_slug']) ? 'support' : $item['menu_slug']; if ( 'pricing' === $submenu_name && ! $fs->is_pricing_page_visible() ) { continue; } if ( ! $show_settings_with_tabs || ! $fs->is_submenu_item_visible( $submenu_name, true ) ) { continue; } } $url = $fs->_get_admin_page_url( $item['menu_slug'] ); $title = $item['menu_title']; $tab = array( 'label' => $title, 'href' => $url, 'slug' => $item['menu_slug'], ); if ( 'pricing' === $item['menu_slug'] && $fs->is_in_trial_promotion() ) { $tab['href'] .= '&trial=true'; } $tabs[] = $tab; } } ?> freemius/templates/tabs-capture-js.php000064400000003356147600046700014111 0ustar00get_slug(); ?> freemius/templates/sticky-admin-notice-js.php000064400000002364147600046700015370 0ustar00 freemius/templates/secure-https-header.php000064400000002022147600046700014746 0ustar00
    get_text_inline( 'Secure HTTPS %s page, running from an external domain', 'secure-x-page-header' ), $VARS['page'] ) ) . ' - ' . sprintf( '%s', 'https://www.mcafeesecure.com/verify?host=' . WP_FS__ROOT_DOMAIN_PRODUCTION, 'Freemius Inc. [US]' ); } ?>
    freemius/templates/pricing.php000064400000010052147600046700012527 0ustar00get_slug(); $timestamp = time(); $context_params = array( 'plugin_id' => $fs->get_id(), 'plugin_public_key' => $fs->get_public_key(), 'plugin_version' => $fs->get_plugin_version(), ); $bundle_id = $fs->get_bundle_id(); if ( ! is_null( $bundle_id ) ) { $context_params['bundle_id'] = $bundle_id; } // Get site context secure params. if ( $fs->is_registered() ) { $context_params = array_merge( $context_params, FS_Security::instance()->get_context_params( $fs->get_site(), $timestamp, 'upgrade' ) ); } else { $context_params['home_url'] = home_url(); } if ( $fs->is_payments_sandbox() ) // Append plugin secure token for sandbox mode authentication.) { $context_params['sandbox'] = FS_Security::instance()->get_secure_token( $fs->get_plugin(), $timestamp, 'checkout' ); } $query_params = array_merge( $context_params, $_GET, array( 'next' => $fs->_get_sync_license_url( false, false ), 'plugin_version' => $fs->get_plugin_version(), // Billing cycle. 'billing_cycle' => fs_request_get( 'billing_cycle', WP_FS__PERIOD_ANNUALLY ), 'is_network_admin' => fs_is_network_admin() ? 'true' : 'false', 'currency' => $fs->apply_filters( 'default_currency', 'usd' ), 'discounts_model' => $fs->apply_filters( 'pricing/discounts_model', 'absolute' ), ) ); $pricing_js_url = fs_asset_url( $fs->get_pricing_js_path() ); wp_enqueue_script( 'freemius-pricing', $pricing_js_url ); $pricing_css_path = $fs->apply_filters( 'pricing/css_path', null ); if ( is_string( $pricing_css_path ) ) { wp_enqueue_style( 'freemius-pricing', fs_asset_url( $pricing_css_path ) ); } $has_tabs = $fs->_add_tabs_before_content(); if ( $has_tabs ) { $query_params['tabs'] = 'true'; } ?>
    $fs->contact_url(), 'is_production' => ( defined( 'WP_FS__IS_PRODUCTION_MODE' ) ? WP_FS__IS_PRODUCTION_MODE : null ), 'menu_slug' => $fs->get_menu_slug(), 'mode' => 'dashboard', 'fs_wp_endpoint_url' => WP_FS__ADDRESS, 'request_handler_url' => admin_url( 'admin-ajax.php?' . http_build_query( array( 'module_id' => $fs->get_id(), 'action' => $fs->get_ajax_action( 'pricing_ajax_action' ), 'security' => $fs->get_ajax_security( 'pricing_ajax_action' ) ) ) ), 'selector' => '#fs_pricing_wrapper', 'unique_affix' => $fs->get_unique_affix(), 'show_annual_in_monthly' => $fs->apply_filters( 'pricing/show_annual_in_monthly', true ), 'license' => $fs->has_active_valid_license() ? $fs->_get_license() : null, 'plugin_icon' => $fs->get_local_icon_url(), 'disable_single_package' => $fs->apply_filters( 'pricing/disable_single_package', false ), ), $query_params ); wp_add_inline_script( 'freemius-pricing', 'Freemius.pricing.new( ' . json_encode( $pricing_config ) . ' )' ); ?>
    _add_tabs_after_content(); } freemius/templates/plugin-icon.php000064400000001024147600046700013317 0ustar00
    freemius/templates/index.php000064400000000127147600046700012205 0ustar00 freemius/templates/email.php000064400000002007147600046700012164 0ustar00 $section ) { ?> $row ) { $col_count = count( $row ); ?>
    :
    freemius/templates/debug.php000064400000117505147600046700012175 0ustar00

    newest->version ?>

    get_option( 'ms_migration_complete', false, true ) ) : ?>
    Resolve Clone(s)
    'WP_FS__REMOTE_ADDR', 'val' => WP_FS__REMOTE_ADDR, ), array( 'key' => 'WP_FS__ADDRESS_PRODUCTION', 'val' => WP_FS__ADDRESS_PRODUCTION, ), array( 'key' => 'FS_API__ADDRESS', 'val' => FS_API__ADDRESS, ), array( 'key' => 'FS_API__SANDBOX_ADDRESS', 'val' => FS_API__SANDBOX_ADDRESS, ), array( 'key' => 'WP_FS__DIR', 'val' => WP_FS__DIR, ), array( 'key' => 'wp_using_ext_object_cache()', 'val' => wp_using_ext_object_cache() ? 'true' : 'false', ), ) ?>
    >

    plugins as $sdk_path => $data ) : ?> version ) ?> >
    version ?> plugin_path ?>
    get_option( $module_type . 's' ), FS_Plugin::get_class_name() ) ?> 0 ) : ?>

    $data ) : ?> file ); } else { $current_theme = wp_get_theme(); $is_active = ( $current_theme->stylesheet === $data->file ); if ( ! $is_active && is_child_theme() ) { $parent_theme = $current_theme->parent(); $is_active = ( ( $parent_theme instanceof WP_Theme ) && $parent_theme->stylesheet === $data->file ); } } ?> id ); $active_modules_by_id[ $data->id ] = true; } ?> has_api_connectivity(); if ( true === $has_api_connectivity && $fs->is_on() ) { echo ' style="background: #E6FFE6; font-weight: bold"'; } else { echo ' style="background: #ffd0d0; font-weight: bold"'; } } ?>> > is_on() ) { echo ' style="color: red; text-transform: uppercase;"'; } ?>>is_on() ? $on_text : $off_text ); } ?> get_network_install_blog_id(); $network_user = $fs->get_network_user(); } ?>
    id ?> version ?> title ?> file ?> public_key ?> email; } ?> has_trial_plan() ) : ?>
    is_registered() ) : ?> is_network_upgrade_mode() ) : ?>
    0 ) : ?>

    /

    $sites ) : ?> blog_id : null; if ( is_null( $site_url ) || $is_multisite ) { $site_url = Freemius::get_unfiltered_site_url( $blog_id, true, true ); } $is_active_clone = ( $site->is_clone( $site_url ) && isset( $active_modules_by_id[ $site->plugin_id ] ) ); if ( $is_active_clone ) { $has_any_active_clone = true; } ?>
    id ?> url ) ?> user_id ?> license_id) ? $site->license_id : '' ?> plan_id ) ) { if ( false === $all_plans ) { $option_name = 'plans'; if ( WP_FS__MODULE_TYPE_PLUGIN !== $module_type ) { $option_name = $module_type . '_' . $option_name; } $all_plans = fs_get_entities( $fs_options->get_option( $option_name, array() ), FS_Plugin_Plan::get_class_name() ); } foreach ( $all_plans[ $slug ] as $plan ) { $plan_id = Freemius::_decrypt( $plan->id ); if ( $site->plan_id == $plan_id ) { $plan_name = Freemius::_decrypt( $plan->name ); break; } } } echo $plan_name; ?> public_key ?> is_whitelabeled ? FS_Plugin_License::mask_secret_key_for_html( $site->secret_key ) : esc_html( $site->secret_key ); ?>
    $plugin_addons ) : ?>

    id ?> title ?> slug ?> version ?> public_key ?> secret_key ) ?>
    id ] = true; } } foreach ( $module_types as $module_type ) { /** * @var FS_Plugin_License[] $licenses */ $licenses = $VARS[ $module_type . '_licenses' ]; foreach ( $licenses as $license ) { if ( $license->is_whitelabeled ) { $users_with_developer_license_by_id[ $license->user_id ] = true; } } } ?>

    $user ) : ?>
    id ?> get_name() ?> email ?> is_verified ) ?> public_key ?> secret_key) : esc_html( $user->secret_key ) ?>
    0 ) : ?>

    id ?> plugin_id ?> user_id ?> plan_id ?> is_unlimited() ? 'Unlimited' : ( $license->is_single_site() ? 'Single Site' : $license->quota ) ?> activated ?> is_block_features ? 'Blocking' : 'Flexible' ?> is_whitelabeled ? 'Whitelabeled' : 'Normal' ?> is_whitelabeled || ! isset( $user_ids_map[ $license->user_id ] ) ) ? $license->get_html_escaped_masked_secret_key() : esc_html( $license->secret_key ); ?> expiration ?>

    #
    {$log.log_order}. {$log.type} {$log.logger} {$log.function} {$log.message_short}
    {$log.message}
    {$log.file}:{$log.line} {$log.created}
    freemius/templates/contact.php000064400000007123147600046700012534 0ustar00get_slug(); $query_params = FS_Contact_Form_Manager::instance()->get_query_params( $fs ); $view_params = array( 'id' => $VARS['id'], 'page' => strtolower( $fs->get_text_inline( 'Contact', 'contact' ) ), ); fs_require_once_template('secure-https-header.php', $view_params); $has_tabs = $fs->_add_tabs_before_content(); if ( $has_tabs ) { $query_params['tabs'] = 'true'; } ?>
    _add_tabs_after_content(); } freemius/templates/connect.php000064400000125571147600046700012542 0ustar00get_slug(); $is_pending_activation = $fs->is_pending_activation(); $is_premium_only = $fs->is_only_premium(); $has_paid_plans = $fs->has_paid_plan(); $is_premium_code = $fs->is_premium(); $is_freemium = $fs->is_freemium(); $fs->_enqueue_connect_essentials(); /** * Enqueueing the styles in `_enqueue_connect_essentials()` is too late, as we need them in the HEADER. Therefore, inject the styles inline to avoid FOUC. * * @author Vova Feldman (@svovaf) */ echo "\n"; $current_user = Freemius::_get_current_wp_user(); $first_name = $current_user->user_firstname; if ( empty( $first_name ) ) { $first_name = $current_user->nickname; } $site_url = Freemius::get_unfiltered_site_url(); $protocol_pos = strpos( $site_url, '://' ); if ( false !== $protocol_pos ) { $site_url = substr( $site_url, $protocol_pos + 3 ); } $freemius_usage_tracking_url = $fs->get_usage_tracking_terms_url(); $freemius_plugin_terms_url = $fs->get_eula_url(); $error = fs_request_get( 'error' ); $has_release_on_freemius = $fs->has_release_on_freemius(); $require_license_key = $is_premium_only || ( $is_freemium && ( $is_premium_code || ! $has_release_on_freemius ) && fs_request_get_bool( 'require_license', ( $is_premium_code || $has_release_on_freemius ) ) ); $freemius_activation_terms_url = ($fs->is_premium() && $require_license_key) ? $fs->get_license_activation_terms_url() : $freemius_usage_tracking_url; $freemius_activation_terms_html = 'freemius.com'; if ( $is_pending_activation ) { $require_license_key = false; } if ( $require_license_key ) { $fs->_add_license_activation_dialog_box(); } $is_optin_dialog = ( $fs->is_theme() && $fs->is_themes_page() && $fs->show_opt_in_on_themes_page() ); if ( $is_optin_dialog ) { $show_close_button = false; $previous_theme_activation_url = ''; if ( ! $is_premium_code ) { $show_close_button = true; } else if ( $is_premium_only ) { $previous_theme_activation_url = $fs->get_previous_theme_activation_url(); $show_close_button = ( ! empty( $previous_theme_activation_url ) ); } } $is_network_level_activation = ( fs_is_network_admin() && $fs->is_network_active() && ! $fs->is_network_delegated_connection() ); $fs_user = Freemius::_get_user_by_email( $current_user->user_email ); $activate_with_current_user = ( is_object( $fs_user ) && ! $is_pending_activation && // If requires a license for activation, use the user associated with the license for the opt-in. ! $require_license_key && ! $is_network_level_activation ); $optin_params = $fs->get_opt_in_params( array(), $is_network_level_activation ); $sites = isset( $optin_params['sites'] ) ? $optin_params['sites'] : array(); $is_network_upgrade_mode = ( fs_is_network_admin() && $fs->is_network_upgrade_mode() ); /* translators: %s: name (e.g. Hey John,) */ $hey_x_text = esc_html( sprintf( fs_text_x_inline( 'Hey %s,', 'greeting', 'hey-x', $slug ), $first_name ) ); $activation_state = array( 'is_license_activation' => $require_license_key, 'is_pending_activation' => $is_pending_activation, 'is_gdpr_required' => true, 'is_network_level_activation' => $is_network_level_activation, 'is_dialog' => $is_optin_dialog, ); ?>
    do_action( 'connect/before', $activation_state ); ?>
    $fs->get_id(), 'size' => $size, ); fs_require_once_template( 'plugin-icon.php', $vars ); ?>
    do_action( 'connect/before_message', $activation_state ) ?>
    apply_filters( 'connect_error_esc_html', esc_html( $error ) ) ?>
    is_plugin_update() ) { echo $fs->apply_filters( 'connect-header', sprintf( '

    %s

    ', esc_html( fs_text_inline( 'Never miss an important update', 'connect-header' ) ) ) ); } else { echo $fs->apply_filters( 'connect-header_on-update', sprintf( '

    %s

    ', sprintf( esc_html( /* translators: %1$s: plugin name (e.g., "Awesome Plugin"); %2$s: version (e.g., "1.2.3") */ fs_text_inline('Thank you for updating to %1$s v%2$s!', 'connect-header_on-update' ) ), esc_html( $fs->get_plugin_name() ), $fs->get_plugin_version() ) ) ); } } ?>

    apply_filters( 'pending_activation_message', sprintf( /* translators: %s: name (e.g. Thanks John!) */ fs_text_inline( 'Thanks %s!', 'thanks-x', $slug ) . '
    ' . fs_text_inline( 'You should receive a confirmation email for %s to your mailbox at %s. Please make sure you click the button in that email to %s.', 'pending-activation-message', $slug ), $first_name, '' . $fs->get_plugin_name() . '', '' . $current_user->user_email . '', fs_text_inline( 'complete the opt-in', 'complete-the-opt-in', $slug ) ) ); } else if ( $require_license_key ) { $button_label = fs_text_inline( 'Activate License', 'activate-license', $slug ); $message = $fs->apply_filters( 'connect-message_on-premium', sprintf( fs_text_inline( 'Welcome to %s! To get started, please enter your license key:', 'thanks-for-purchasing', $slug ), '' . $fs->get_plugin_name() . '' ), $first_name, $fs->get_plugin_name() ); } else { $filter = 'connect_message'; if ( ! $fs->is_plugin_update() ) { $default_optin_message = esc_html( sprintf( /* translators: %s: module type (plugin, theme, or add-on) */ fs_text_inline( 'Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info. This will help us make the %s more compatible with your site and better at doing what you need it to.', 'connect-message', $slug ), $fs->get_module_label( true ) ) ); } else { // If Freemius was added on a plugin update, set different // opt-in message. /* translators: %s: module type (plugin, theme, or add-on) */ $default_optin_message = esc_html( sprintf( fs_text_inline( 'We have introduced this opt-in so you never miss an important update and help us make the %s more compatible with your site and better at doing what you need it to.', 'connect-message_on-update_why' ), $fs->get_module_label( true ) ) ); $default_optin_message .= '

    ' . esc_html( fs_text_inline( 'Opt in to get email notifications for security & feature updates, educational content, and occasional offers, and to share some basic WordPress environment info.', 'connect-message_on-update', $slug ) ); if ( $fs->is_enable_anonymous() ) { $default_optin_message .= ' ' . esc_html( fs_text_inline( 'If you skip this, that\'s okay! %1$s will still work just fine.', 'connect-message_on-update_skip', $slug ) ); } // If user customized the opt-in message on update, use // that message. Otherwise, fallback to regular opt-in // custom message if exists. if ( $fs->has_filter( 'connect_message_on_update' ) ) { $filter = 'connect_message_on_update'; } } $message = $fs->apply_filters( $filter, sprintf( $default_optin_message, '' . esc_html( $fs->get_plugin_name() ) . '', '' . $current_user->user_login . '', '' . $site_url . '', $freemius_activation_terms_html ), $first_name, $fs->get_plugin_name(), $current_user->user_login, '' . $site_url . '', $freemius_activation_terms_html, true ); } if ( $is_network_upgrade_mode ) { $network_integration_text = esc_html( fs_text_inline( 'We\'re excited to introduce the Freemius network-level integration.', 'connect_message_network_upgrade', $slug ) ); if ($is_premium_code){ $message = $network_integration_text . ' ' . sprintf( fs_text_inline( 'During the update process we detected %d site(s) that are still pending license activation.', 'connect_message_network_upgrade-premium', $slug ), count( $sites ) ); $message .= '

    ' . sprintf( fs_text_inline( 'If you\'d like to use the %s on those sites, please enter your license key below and click the activation button.', 'connect_message_network_upgrade-premium-activate-license', $slug ), $is_premium_only ? $fs->get_module_label( true ) : sprintf( /* translators: %s: module type (plugin, theme, or add-on) */ fs_text_inline( "%s's paid features", 'x-paid-features', $slug ), $fs->get_module_label( true ) ) ); /* translators: %s: module type (plugin, theme, or add-on) */ $message .= ' ' . sprintf( fs_text_inline( 'Alternatively, you can skip it for now and activate the license later, in your %s\'s network-level Account page.', 'connect_message_network_upgrade-premium-skip-license', $slug ), $fs->get_module_label( true ) ); }else { $message = $network_integration_text . ' ' . sprintf( fs_text_inline( 'During the update process we detected %s site(s) in the network that are still pending your attention.', 'connect_message_network_upgrade-free', $slug ), count( $sites ) ) . '

    ' . ( fs_starts_with( $message, $hey_x_text . '
    ' ) ? substr( $message, strlen( $hey_x_text . '
    ' ) ) : $message ); } } echo $message; ?>

    do_action( 'connect/after_license_input', $activation_state ); ?> - %s', $fs->get_text_inline( 'Yes', 'yes' ), $fs->get_text_inline( 'send me security & feature updates, educational content and offers.', 'send-updates' ) ); $do_not_send_updates_text = sprintf( '%s - %s', $fs->get_text_inline( 'No', 'no' ), sprintf( $fs->get_text_inline( 'do %sNOT%s send me security & feature updates, educational content and offers.', 'do-not-send-updates' ), '', '' ) ); ?>
    $fs->get_id(), 'sites' => $sites, 'require_license_key' => $require_license_key ); echo fs_get_template( 'partials/network-activation.php', $vars ); ?> do_action( 'connect/after_message', $activation_state ) ?>
    do_action( 'connect/before_actions', $activation_state ) ?> is_enable_anonymous() && ! $is_pending_activation && ( ! $require_license_key || $is_network_upgrade_mode ) ) : ?> apply_filters( 'show_delegation_option', true ) ) : ?>
    get_unique_affix() . '_activate_existing' ) ?>
    $value ) : ?>
    do_action( 'connect/after_actions', $activation_state ) ?>
    is_permission_requested( 'newsletter' ) ) { $permissions[] = $permission_manager->get_newsletter_permission(); } $permissions = $permission_manager->get_permissions( $require_license_key, $permissions ); if ( ! empty( $permissions ) ) : ?>

    do_action( 'connect/after', $activation_state ); if ( $is_optin_dialog ) { ?>
    freemius/templates/checkout.php000064400000001334147600046700012704 0ustar00is_premium() ) { fs_require_template( 'checkout/frame.php', $VARS ); } else { fs_require_template( 'checkout/redirect.php', $VARS ); } } freemius/templates/auto-installation.php000064400000016336147600046700014556 0ustar00is_tracking_allowed() ? 'stop_tracking' : 'allow_tracking'; $title = $fs->get_plugin_title(); if ( $plugin_id != $fs->get_id() ) { $addon = $fs->get_addon( $plugin_id ); if ( is_object( $addon ) ) { $title = $addon->title . ' ' . fs_text_inline( 'Add-On', 'addon', $slug ); } } $plugin_title = sprintf( '%s', esc_html( $title ) ); $sec_countdown = 30; $countdown_html = sprintf( esc_js( /* translators: %s: Number of seconds */ fs_text_inline( '%s sec', 'x-sec', $slug ) ), sprintf( '%s', $sec_countdown ) ); fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' ); fs_enqueue_local_style( 'fs_common', '/admin/common.css' ); $params = array(); $loader_html = fs_get_template( 'ajax-loader.php', $params ); // Pass unique auto installation URL if WP_Filesystem is needed. $install_url = $fs->_get_sync_license_url( $plugin_id, true, array( 'auto_install' => 'true' ) ); ob_start(); $method = ''; // Leave blank so WP_Filesystem can populate it as necessary. $credentials = request_filesystem_credentials( esc_url_raw( $install_url ), $method, false, WP_PLUGIN_DIR, array() ); $credentials_form = ob_get_clean(); $require_credentials = ! empty( $credentials_form ); ?>

    %s', 'https://freemius.com', 'freemius.com' ), $countdown_html ) ?>

    ' freemius/templates/api-connectivity-message-js.php000064400000002016147600046700016416 0ustar00 freemius/templates/ajax-loader.php000064400000000374147600046700013271 0ustar00 freemius/templates/admin-notice.php000064400000005143147600046700013450 0ustar00
    >
    freemius/templates/add-trial-to-pricing.php000064400000001432147600046700015010 0ustar00 freemius/templates/add-ons.php000064400000054767147600046700012446 0ustar00get_slug(); $open_addon_slug = fs_request_get( 'slug' ); $open_addon = false; $is_data_debug_mode = $fs->is_data_debug_mode(); $is_whitelabeled = $fs->is_whitelabeled(); /** * @var FS_Plugin[] */ $addons = $fs->get_addons(); $has_addons = ( is_array( $addons ) && 0 < count( $addons ) ); $account_addon_ids = $fs->get_updated_account_addons(); $download_latest_text = fs_text_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $slug ); $view_details_text = fs_text_inline( 'View details', 'view-details', $slug ); $has_tabs = $fs->_add_tabs_before_content(); $fs_blog_id = ( is_multisite() && ! is_network_admin() ) ? get_current_blog_id() : 0; ?>

    get_plugin_name() ) ) ?>

    do_action( 'addons/after_title' ) ?>

      _get_addons_plans_and_pricing_map_by_id(); $active_plugins_directories_map = Freemius::get_active_plugins_directories_map( $fs_blog_id ); ?> is_whitelabeled_by_flag() ) { $hide_all_addons_data = true; $addon_ids = $fs->get_updated_account_addons(); $installed_addons = $fs->get_installed_addons(); foreach ( $installed_addons as $fs_addon ) { $addon_ids[] = $fs_addon->get_id(); } if ( ! empty( $addon_ids ) ) { $addon_ids = array_unique( $addon_ids ); } foreach ( $addon_ids as $addon_id ) { $addon = $fs->get_addon( $addon_id ); if ( ! is_object( $addon ) ) { continue; } $addon_storage = FS_Storage::instance( WP_FS__MODULE_TYPE_PLUGIN, $addon->slug ); if ( ! $addon_storage->is_whitelabeled ) { $hide_all_addons_data = false; break; } if ( $is_data_debug_mode ) { $is_whitelabeled = false; } } } ?> get_addon_basename( $addon->id ); $is_addon_installed = file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $basename ) ); if ( ! $is_addon_installed && $hide_all_addons_data ) { continue; } $is_addon_activated = $is_addon_installed ? $fs->is_addon_activated( $addon->id ) : false; $is_plugin_active = ( $is_addon_activated || isset( $active_plugins_directories_map[ dirname( $basename ) ] ) ); $open_addon = ( $open_addon || ( $open_addon_slug === $addon->slug ) ); $price = 0; $has_trial = false; $has_free_plan = false; $has_paid_plan = false; if ( isset( $plans_and_pricing_by_addon_id[$addon->id] ) ) { $plans = $plans_and_pricing_by_addon_id[$addon->id]; if ( is_array( $plans ) && 0 < count( $plans ) ) { foreach ( $plans as $plan ) { if ( ! isset( $plan->pricing ) || ! is_array( $plan->pricing ) || 0 == count( $plan->pricing ) ) { // No pricing means a free plan. $has_free_plan = true; continue; } $has_paid_plan = true; $has_trial = $has_trial || ( is_numeric( $plan->trial_period ) && ( $plan->trial_period > 0 ) ); $min_price = 999999; foreach ( $plan->pricing as $pricing ) { $pricing = new FS_Pricing( $pricing ); if ( ! $pricing->is_usd() ) { /** * Skip non-USD pricing. * * @author Leo Fajardo (@leorw) * @since 2.3.1 */ continue; } if ( $pricing->has_annual() ) { $min_price = min( $min_price, $pricing->annual_price ); } else if ( $pricing->has_monthly() ) { $min_price = min( $min_price, 12 * $pricing->monthly_price ); } } if ( $min_price < 999999 ) { $price = $min_price; } } } if ( ! $has_paid_plan && ! $has_free_plan ) { continue; } } ?>
    • get_id() . '&plugin=' . $addon->slug . '&TB_iframe=true&width=600&height=550' ) ), esc_attr( sprintf( fs_text_inline( 'More information about %s', 'more-information-about-x', $slug ), $addon->title ) ), esc_attr( $addon->title ) ) . ' class="thickbox%s">%s'; echo sprintf( $view_details_link, /** * Additional class. * * @author Leo Fajardo (@leorw) * @since 2.2.4 */ ' fs-overlay', /** * Set the view details link text to an empty string since it is an overlay that * doesn't really need a text and whose purpose is to open the details dialog when * the card is clicked. * * @author Leo Fajardo (@leorw) * @since 2.2.4 */ '' ); ?> info ) ) { $addon->info = new stdClass(); } if ( ! isset( $addon->info->card_banner_url ) ) { $addon->info->card_banner_url = '//dashboard.freemius.com/assets/img/marketing/blueprint-300x100.jpg'; } if ( ! isset( $addon->info->short_description ) ) { $addon->info->short_description = 'What\'s the one thing your add-on does really, really well?'; } ?>
      • %s', esc_html( $is_plugin_active ? fs_text_x_inline( 'Active', 'active add-on', 'active-addon', $slug ) : fs_text_x_inline( 'Installed', 'installed add-on', 'installed-addon', $slug ) ) ); } ?>
      • title ?>
      • 0) $descriptors[] = '$' . number_format( $price, 2 ); if ($has_trial) $descriptors[] = fs_text_x_inline( 'Trial', 'trial period', 'trial', $slug ); echo implode(' - ', $descriptors); } ?>
      • info->short_description ) ? $addon->info->short_description : 'SHORT DESCRIPTION' ?>
      • is_wp_org_compliant ); $is_allowed_to_install = ( $fs->is_allowed_to_install() || $is_free_only_wp_org_compliant ); $show_premium_activation_or_installation_action = true; if ( ! in_array( $addon->id, $account_addon_ids ) ) { $show_premium_activation_or_installation_action = false; } else if ( $is_addon_installed ) { /** * If any add-on's version (free or premium) is installed, check if the * premium version can be activated and show the relevant action. Otherwise, * show the relevant action for the free version. * * @author Leo Fajardo (@leorw) * @since 2.4.5 */ $fs_addon = $is_addon_activated ? $fs->get_addon_instance( $addon->id ) : null; $premium_plugin_basename = is_object( $fs_addon ) ? $fs_addon->premium_plugin_basename() : "{$addon->premium_slug}/{$addon->slug}.php"; if ( ( $is_addon_activated && $fs_addon->is_premium() ) || file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $premium_plugin_basename ) ) ) { $basename = $premium_plugin_basename; } $show_premium_activation_or_installation_action = ( ( ! $is_addon_activated || ! $fs_addon->is_premium() ) && /** * This check is needed for cases when an active add-on doesn't have an * associated Freemius instance. * * @author Leo Fajardo (@leorw) * @since 2.4.5 */ ( ! $is_plugin_active ) ); } ?>
      • _get_latest_download_local_url( $addon->id ); ?>
      • %s', wp_nonce_url( self_admin_url( 'update.php?' . ( ( $has_paid_plan || ! $addon->is_wp_org_compliant ) ? 'fs_allow_updater_and_dialog=true&' : '' ) . 'action=install-plugin&plugin=' . $addon->slug ), 'install-plugin_' . $addon->slug ), fs_esc_html_inline( 'Install Now', 'install-now', $slug ) ); } else { echo sprintf( '%s', wp_nonce_url( 'plugins.php?action=activate&plugin=' . $basename, 'activate-plugin_' . $basename ), fs_esc_attr_inline( 'Activate this add-on', 'activate-this-addon', $addon->slug ), fs_text_inline( 'Activate', 'activate', $addon->slug ) ); } ?>
    do_action( 'addons/after_addons' ) ?>
    _add_tabs_after_content(); } freemius/templates/account.php000064400000177042147600046700012545 0ustar00get_slug(); /** * @var FS_Plugin_Tag $update */ $update = $fs->has_release_on_freemius() ? $fs->get_update( false, false ) : null; if ( is_object($update) ) { /** * This logic is particularly required for multisite environment. * If a module is site activated (not network) and not on the main site, * the module will NOT be executed on the network level, therefore, the * custom updates logic will not be executed as well, so unless we force * the injection of the update into the updates transient, premium updates * will not work. * * @author Vova Feldman (@svovaf) * @since 2.0.0 */ $updater = FS_Plugin_Updater::instance( $fs ); $updater->set_update_data( $update ); } $is_paying = $fs->is_paying(); $user = $fs->get_user(); $site = $fs->get_site(); $name = $user->get_name(); $license = $fs->_get_license(); $is_license_foreign = ( is_object( $license ) && $user->id != $license->user_id ); $is_data_debug_mode = $fs->is_data_debug_mode(); $is_whitelabeled = $fs->is_whitelabeled(); $subscription = ( is_object( $license ) ? $fs->_get_subscription( $license->id ) : null ); $plan = $fs->get_plan(); $is_active_subscription = ( is_object( $subscription ) && $subscription->is_active() ); $is_paid_trial = $fs->is_paid_trial(); $has_paid_plan = $fs->apply_filters( 'has_paid_plan_account', $fs->has_paid_plan() ); $show_upgrade = ( ! $is_whitelabeled && $has_paid_plan && ! $is_paying && ! $is_paid_trial ); $trial_plan = $fs->get_trial_plan(); $is_plan_change_supported = ( ! $fs->is_single_plan() && ! $fs->apply_filters( 'hide_plan_change', false ) ); if ( $has_paid_plan ) { $fs->_add_license_activation_dialog_box(); } if ( $fs->should_handle_user_change() ) { $fs->_add_email_address_update_dialog_box(); } $ids_of_installs_activated_with_foreign_licenses = $fs->should_handle_user_change() ? $fs->get_installs_ids_with_foreign_licenses() : array(); if ( ! empty( $ids_of_installs_activated_with_foreign_licenses ) ) { $fs->_add_user_change_dialog_box( $ids_of_installs_activated_with_foreign_licenses ); } if ( $fs->is_whitelabeled( true ) || $fs->is_data_debug_mode() ) { $fs->_add_data_debug_mode_dialog_box(); } if ( fs_request_get_bool( 'auto_install' ) ) { $fs->_add_auto_installation_dialog_box(); } if ( fs_request_get_bool( 'activate_license' ) ) { // Open the license activation dialog box on the account page. add_action( 'admin_footer', array( &$fs, '_open_license_activation_dialog_box' ) ); } $show_billing = ( ! $is_whitelabeled && ! $fs->apply_filters( 'hide_billing_and_payments_info', false ) ); if ( $show_billing ) { $payments = $fs->_fetch_payments(); $show_billing = ( is_array( $payments ) && 0 < count( $payments ) ); } $has_tabs = $fs->_add_tabs_before_content(); // Aliases. $download_latest_text = fs_text_x_inline( 'Download Latest', 'as download latest version', 'download-latest', $slug ); $downgrading_plan_text = fs_text_inline( 'Downgrading your plan', 'downgrading-plan', $slug ); $cancelling_subscription_text = fs_text_inline( 'Cancelling the subscription', 'cancelling-subscription', $slug ); /* translators: %1$s: Either 'Downgrading your plan' or 'Cancelling the subscription' */ $downgrade_x_confirm_text = fs_text_inline( '%1$s will immediately stop all future recurring payments and your %2$s plan license will expire in %3$s.', 'downgrade-x-confirm', $slug ); $prices_increase_text = fs_text_inline( 'Please note that we will not be able to grandfather outdated pricing for renewals/new subscriptions after a cancellation. If you choose to renew the subscription manually in the future, after a price increase, which typically occurs once a year, you will be charged the updated price.', 'pricing-increase-warning', $slug ); $cancel_trial_confirm_text = fs_text_inline( 'Cancelling the trial will immediately block access to all premium features. Are you sure?', 'cancel-trial-confirm', $slug ); $after_downgrade_non_blocking_text = fs_text_inline( 'You can still enjoy all %s features but you will not have access to %s security & feature updates, nor support.', 'after-downgrade-non-blocking', $slug ); $after_downgrade_blocking_text = fs_text_inline( 'Once your license expires you can still use the Free version but you will NOT have access to the %s features.', 'after-downgrade-blocking', $slug ); /* translators: %s: Plan title (e.g. "Professional") */ $activate_plan_text = fs_text_inline( 'Activate %s Plan', 'activate-x-plan', $slug ); $version_text = fs_text_x_inline( 'Version', 'product version', 'version', $slug ); /* translators: %s: Time period (e.g. Auto renews in "2 months") */ $renews_in_text = fs_text_inline( 'Auto renews in %s', 'renews-in', $slug ); /* translators: %s: Time period (e.g. Expires in "2 months") */ $expires_in_text = fs_text_inline( 'Expires in %s', 'expires-in', $slug ); $sync_license_text = fs_text_x_inline( 'Sync License', 'as synchronize license', 'sync-license', $slug ); $cancel_trial_text = fs_text_inline( 'Cancel Trial', 'cancel-trial', $slug ); $change_plan_text = fs_text_inline( 'Change Plan', 'change-plan', $slug ); $upgrade_text = fs_text_x_inline( 'Upgrade', 'verb', 'upgrade', $slug ); $addons_text = fs_text_inline( 'Add-Ons', 'add-ons', $slug ); $downgrade_text = fs_text_x_inline( 'Downgrade', 'verb', 'downgrade', $slug ); $trial_text = fs_text_x_inline( 'Trial', 'trial period', 'trial', $slug ); $free_text = fs_text_inline( 'Free', 'free', $slug ); $activate_text = fs_text_inline( 'Activate', 'activate', $slug ); $plan_text = fs_text_x_inline( 'Plan', 'as product pricing plan', 'plan', $slug ); $bundle_plan_text = fs_text_inline( 'Bundle Plan', 'bundle-plan', $slug ); $show_plan_row = true; $show_license_row = is_object( $license ); $site_view_params = array(); if ( fs_is_network_admin() ) { $sites = Freemius::get_sites(); $all_installs_plan_id = null; $all_installs_license_id = ( $show_license_row ? $license->id : null ); foreach ( $sites as $s ) { $site_info = $fs->get_site_info( $s ); $install = $fs->get_install_by_blog_id( $site_info['blog_id'] ); $view_params = array( 'freemius' => $fs, 'user' => $fs->get_user(), 'license' => $license, 'site' => $site_info, 'install' => $install, ); $site_view_params[] = $view_params; if ( empty( $install ) ) { continue; } if ( $show_plan_row ) { if ( is_null( $all_installs_plan_id ) ) { $all_installs_plan_id = $install->plan_id; } else if ( $all_installs_plan_id != $install->plan_id ) { $show_plan_row = false; } } if ( $show_license_row && $all_installs_license_id != $install->license_id ) { $show_license_row = false; } } } $has_bundle_license = false; if ( is_object( $license ) && FS_Plugin_License::is_valid_id( $license->parent_license_id ) ) { // Context license has a parent license, therefore, the account has a bundle license. $has_bundle_license = true; } $bundle_subscription = null; $is_bundle_first_payment_pending = false; if ( $show_plan_row && is_object( $license ) && $has_bundle_license ) { $bundle_plan_title = strtoupper( $license->parent_plan_title ); $bundle_subscription = $fs->_get_subscription( $license->parent_license_id ); $is_bundle_first_payment_pending = $license->is_first_payment_pending(); } $fs_blog_id = ( is_multisite() && ! is_network_admin() ) ? get_current_blog_id() : 0; $active_plugins_directories_map = Freemius::get_active_plugins_directories_map( $fs_blog_id ); $is_premium = $fs->is_premium(); $account_addons = $fs->get_updated_account_addons(); $installed_addons = $fs->get_installed_addons(); $installed_addons_ids = array(); /** * Store the installed add-ons' IDs into a collection which will be used in determining the add-ons to show on the "Account" page, and at the same time try to find an add-on that is activated with a bundle license if the core product is not. * * @author Leo Fajardo * * @since 2.4.0 */ foreach ( $installed_addons as $fs_addon ) { $installed_addons_ids[] = $fs_addon->get_id(); if ( $has_bundle_license ) { // We already have the context bundle license details, skip. continue; } if ( $show_plan_row && $fs_addon->has_active_valid_license() ) { $addon_license = $fs_addon->_get_license(); if ( FS_Plugin_License::is_valid_id( $addon_license->parent_license_id ) ) { // Add-on's license is associated with a parent/bundle license. $has_bundle_license = true; $bundle_plan_title = strtoupper( $addon_license->parent_plan_title ); $bundle_subscription = $fs_addon->_get_subscription( $addon_license->parent_license_id ); $is_bundle_first_payment_pending = $addon_license->is_first_payment_pending(); } } } $addons_to_show = array_unique( array_merge( $installed_addons_ids, $account_addons ) ); $is_active_bundle_subscription = ( is_object( $bundle_subscription ) && $bundle_subscription->is_active() ); $available_license = ( $fs->is_free_plan() && ! fs_is_network_admin() ) ? $fs->_get_available_premium_license( $site->is_localhost() ) : null; $available_license_paid_plan = is_object( $available_license ) ? $fs->_get_plan_by_id( $available_license->plan_id ) : null; ?>
    apply_filters( 'hide_account_tabs', false ) ) : ?>

    apply_filters( 'hide_license_key', false ) ); $profile = array(); if ( ! $is_whitelabeled ) { $profile[] = array( 'id' => 'user_name', 'title' => fs_text_inline( 'Name', 'name', $slug ), 'value' => $name ); // if (isset($user->email) && false !== strpos($user->email, '@')) $profile[] = array( 'id' => 'email', 'title' => fs_text_inline( 'Email', 'email', $slug ), 'value' => $user->email ); if ( is_numeric( $user->id ) ) { $profile[] = array( 'id' => 'user_id', 'title' => fs_text_inline( 'User ID', 'user-id', $slug ), 'value' => $user->id ); } } $profile[] = array( 'id' => 'product', 'title' => ( $fs->is_plugin() ? fs_text_inline( 'Plugin', 'plugin', $slug ) : fs_text_inline( 'Theme', 'theme', $slug ) ), 'value' => $fs->get_plugin_title() ); $profile[] = array( 'id' => 'product_id', 'title' => ( $fs->is_plugin() ? fs_text_inline( 'Plugin', 'plugin', $slug ) : fs_text_inline( 'Theme', 'theme', $slug ) ) . ' ' . fs_text_inline( 'ID', 'id', $slug ), 'value' => $fs->get_id() ); if ( ! fs_is_network_admin()) { $profile[] = array( 'id' => 'site_id', 'title' => fs_text_inline( 'Site ID', 'site-id', $slug ), 'value' => is_string( $site->id ) ? $site->id : fs_text_inline( 'No ID', 'no-id', $slug ) ); $profile[] = array( 'id' => 'site_public_key', 'title' => fs_text_inline( 'Public Key', 'public-key', $slug ), 'value' => $site->public_key ); $profile[] = array( 'id' => 'site_secret_key', 'title' => fs_text_inline( 'Secret Key', 'secret-key', $slug ), 'value' => ( ( is_string( $site->secret_key ) ) ? $site->secret_key : fs_text_x_inline( 'No Secret', 'as secret encryption key missing', 'no-secret', $slug ) ) ); } $profile[] = array( 'id' => 'version', 'title' => $version_text, 'value' => $fs->get_plugin_version() ); if ( ! fs_is_network_admin() && $is_premium ) { $profile[] = array( 'id' => 'beta_program', 'title' => '', 'value' => $site->is_beta ); } if ( $has_paid_plan || $has_bundle_license ) { if ( $fs->is_trial() ) { if ( $show_plan_row ) { $profile[] = array( 'id' => 'plan', 'title' => $plan_text, 'value' => ( is_string( $trial_plan->name ) ? strtoupper( $trial_plan->title ) : fs_text_inline( 'Trial', 'trial', $slug ) ) ); } } else { if ( $show_plan_row ) { $profile[] = array( 'id' => 'plan', 'title' => ( $has_bundle_license ? ucfirst( $fs->get_module_type() ) . ' ' : '' ) . $plan_text, 'value' => strtoupper( is_string( $plan->name ) ? $plan->title : strtoupper( $free_text ) ) ); if ( $has_bundle_license ) { $profile[] = array( 'id' => 'bundle_plan', 'title' => $bundle_plan_text, 'value' => $bundle_plan_title ); } } if ( is_object( $license ) ) { if ( ! $hide_license_key ) { $profile[] = array( 'id' => 'license_key', 'title' => fs_text_inline( 'License Key', $slug ), 'value' => $license->secret_key, ); } } } } ?> > is_verified() ) : ?> is_trial() ) : ?> is_lifetime() ) : ?> is_first_payment_pending() ) : ?> is_expired() ?> is_first_payment_pending() ) : ?> is_trial() ) : ?>
    $fs, 'slug' => $slug, 'license' => $available_license, 'plan' => $available_license_paid_plan, 'is_localhost' => $site->is_localhost(), 'install_id' => $site->id, 'class' => 'button-primary', ); fs_require_template( 'account/partials/activate-license-button.php', $view_params ); ?>
    get_unique_affix() . '_sync_license' ) ?>
    has_premium_version() ) : ?> can_use_premium_code() ) : ?>
    is_verified() ) : ?>
    has_release_on_freemius() ) : ?> secret_key ) && in_array( $p['id'], array( 'email', 'user_name' ) ) ) ) : ?>

    get_site(); if ( is_object( $site ) && ( ! is_object( $current_install ) || $current_install->id != $site->id ) ) { $fs->switch_to_blog( $current_blog_id, $site, true ); } ?>
    is_whitelabeled_by_flag() ) { $hide_all_addons_data = true; foreach ( $addons_to_show as $addon_id ) { $is_addon_installed = isset( $installed_addons_ids_map[ $addon_id ] ); $addon_info = $fs->_get_addon_info( $addon_id, $is_addon_installed ); $is_addon_connected = $addon_info['is_connected']; $fs_addon = ( $is_addon_connected && $is_addon_installed ) ? freemius( $addon_id ) : null; $is_whitelabeled = is_object( $fs_addon ) ? $fs_addon->is_whitelabeled( true ) : $addon_info['is_whitelabeled']; if ( ! $is_whitelabeled ) { $hide_all_addons_data = false; } if ( $is_data_debug_mode ) { $is_whitelabeled = false; } $addon_info_by_id[ $addon_id ] = $addon_info; } } foreach ( $addons_to_show as $addon_id ) { $is_addon_installed = isset( $installed_addons_ids_map[ $addon_id ] ); if ( $hide_all_addons_data && ! $is_addon_installed && ! file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $fs->get_addon_basename( $addon_id ) ) ) ) { continue; } $addon_view_params = array( 'parent_fs' => $fs, 'addon_id' => $addon_id, 'odd' => $odd, 'fs_blog_id' => $fs_blog_id, 'active_plugins_directories_map' => &$active_plugins_directories_map, 'is_addon_installed' => $is_addon_installed, 'addon_info' => isset( $addon_info_by_id[ $addon_id ] ) ? $addon_info_by_id[ $addon_id ] : $fs->_get_addon_info( $addon_id, $is_addon_installed ), 'is_whitelabeled' => ( $is_whitelabeled && ! $is_data_debug_mode ) ); fs_require_template( 'account/partials/addon.php', $addon_view_params ); $odd = ! $odd; } ?>

    do_action( 'after_account_details' ) ?> $VARS['id'], 'payments' => $payments ); fs_require_once_template( 'account/billing.php', $view_params ); fs_require_once_template( 'account/payments.php', $view_params ); } ?>
    _get_subscription_cancellation_dialog_box_template_params( true ); if ( ! empty( $subscription_cancellation_dialog_box_template_params ) ) { fs_require_template( 'forms/subscription-cancellation.php', $subscription_cancellation_dialog_box_template_params ); } ?> _add_tabs_after_content(); } freemius/start.php000064400000056456147600046700010255 0ustar00=' ) && version_compare( $wp_version, '6.3.1', '<=' ) && ( 'site-editor.php' === basename( $_SERVER['SCRIPT_FILENAME'] ) || ( function_exists( 'wp_is_json_request' ) && wp_is_json_request() && ! empty( $_GET['wp_theme_preview'] ) ) ) ) { // Requiring this file since the call to get_stylesheet() below can trigger a call to wp_get_current_user() when previewing a theme. require_once ABSPATH . 'wp-includes/pluggable.php'; } /** * Get the themes directory where the active theme is located (not passing the stylesheet will make WordPress * assume that the themes directory is inside `wp-content`. * * @author Leo Fajardo (@leorw) * @since 2.2.3 */ $themes_directory = get_theme_root( get_stylesheet() ); $themes_directory_name = basename( $themes_directory ); // This change ensures that the condition works even if the SDK is located in a subdirectory (e.g., vendor) $theme_candidate_sdk_basename = str_replace( $themes_directory . '/' . get_stylesheet() . '/', '', $fs_root_path ); // Check if the current file is part of the active theme. $is_current_sdk_from_active_theme = $file_path == $themes_directory . '/' . get_stylesheet() . '/' . $theme_candidate_sdk_basename . '/' . basename( $file_path ); $is_current_sdk_from_parent_theme = false; // Check if the current file is part of the parent theme. if ( ! $is_current_sdk_from_active_theme ) { $theme_candidate_sdk_basename = str_replace( $themes_directory . '/' . get_template() . '/', '', $fs_root_path ); $is_current_sdk_from_parent_theme = $file_path == $themes_directory . '/' . get_template() . '/' . $theme_candidate_sdk_basename . '/' . basename( $file_path ); } $theme_name = null; if ( $is_current_sdk_from_active_theme ) { $theme_name = get_stylesheet(); $this_sdk_relative_path = '../' . $themes_directory_name . '/' . $theme_name . '/' . $theme_candidate_sdk_basename; $is_theme = true; } else if ( $is_current_sdk_from_parent_theme ) { $theme_name = get_template(); $this_sdk_relative_path = '../' . $themes_directory_name . '/' . $theme_name . '/' . $theme_candidate_sdk_basename; $is_theme = true; } else { $this_sdk_relative_path = plugin_basename( $fs_root_path ); $is_theme = false; /** * If this file was included from another plugin with lower SDK version, and if this plugin is symlinked, then we need to get the actual plugin path, * as the value right now will be wrong, it will only remove the directory separator from the file_path. * * The check of `fs_find_direct_caller_plugin_file` determines that this file was indeed included by a different plugin than the main plugin. */ if ( DIRECTORY_SEPARATOR . $this_sdk_relative_path === $fs_root_path && function_exists( 'fs_find_direct_caller_plugin_file' ) ) { $original_plugin_dir_name = dirname( fs_find_direct_caller_plugin_file( $file_path ) ); // Remove everything before the original plugin directory name. $this_sdk_relative_path = substr( $this_sdk_relative_path, strpos( $this_sdk_relative_path, $original_plugin_dir_name ) ); unset( $original_plugin_dir_name ); } } if ( ! isset( $fs_active_plugins ) ) { // Load all Freemius powered active plugins. $fs_active_plugins = get_option( 'fs_active_plugins' ); if ( ! is_object( $fs_active_plugins ) ) { $fs_active_plugins = new stdClass(); } if ( ! isset( $fs_active_plugins->plugins ) ) { $fs_active_plugins->plugins = array(); } } if ( empty( $fs_active_plugins->abspath ) ) { /** * Store the WP install absolute path reference to identify environment change * while replicating the storage. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 */ $fs_active_plugins->abspath = ABSPATH; } else { if ( ABSPATH !== $fs_active_plugins->abspath ) { /** * WordPress path has changed, cleanup the SDK references cache. * This resolves issues triggered when spinning a staging environments * while replicating the database. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 */ $fs_active_plugins->abspath = ABSPATH; $fs_active_plugins->plugins = array(); unset( $fs_active_plugins->newest ); } else { /** * Make sure SDK references are still valid. This resolves * issues when users hard delete modules via FTP. * * @author Vova Feldman (@svovaf) * @since 1.2.1.7 */ $has_changes = false; foreach ( $fs_active_plugins->plugins as $sdk_path => $data ) { if ( ! file_exists( ( isset( $data->type ) && 'theme' === $data->type ? $themes_directory : WP_PLUGIN_DIR ) . '/' . $sdk_path ) ) { unset( $fs_active_plugins->plugins[ $sdk_path ] ); if ( ! empty( $fs_active_plugins->newest ) && $sdk_path === $fs_active_plugins->newest->sdk_path ) { unset( $fs_active_plugins->newest ); } $has_changes = true; } } if ( $has_changes ) { if ( empty( $fs_active_plugins->plugins ) ) { unset( $fs_active_plugins->newest ); } update_option( 'fs_active_plugins', $fs_active_plugins ); } } } if ( ! function_exists( 'fs_find_direct_caller_plugin_file' ) ) { require_once dirname( __FILE__ ) . '/includes/supplements/fs-essential-functions-1.1.7.1.php'; } if ( ! function_exists( 'fs_get_plugins' ) ) { require_once dirname( __FILE__ ) . '/includes/supplements/fs-essential-functions-2.2.1.php'; } // Update current SDK info based on the SDK path. if ( ! isset( $fs_active_plugins->plugins[ $this_sdk_relative_path ] ) || $this_sdk_version != $fs_active_plugins->plugins[ $this_sdk_relative_path ]->version ) { if ( $is_theme ) { // Saving relative path and not only directory name as it could be a subfolder $plugin_path = $theme_name; } else { $plugin_path = plugin_basename( fs_find_direct_caller_plugin_file( $file_path ) ); } $fs_active_plugins->plugins[ $this_sdk_relative_path ] = (object) array( 'version' => $this_sdk_version, 'type' => ( $is_theme ? 'theme' : 'plugin' ), 'timestamp' => time(), 'plugin_path' => $plugin_path, ); } $is_current_sdk_newest = isset( $fs_active_plugins->newest ) && ( $this_sdk_relative_path == $fs_active_plugins->newest->sdk_path ); if ( ! isset( $fs_active_plugins->newest ) ) { /** * This will be executed only once, for the first time a Freemius powered plugin is activated. */ fs_update_sdk_newest_version( $this_sdk_relative_path, $fs_active_plugins->plugins[ $this_sdk_relative_path ]->plugin_path ); $is_current_sdk_newest = true; } else if ( version_compare( $fs_active_plugins->newest->version, $this_sdk_version, '<' ) ) { /** * Current SDK is newer than the newest stored SDK. */ fs_update_sdk_newest_version( $this_sdk_relative_path, $fs_active_plugins->plugins[ $this_sdk_relative_path ]->plugin_path ); if ( class_exists( 'Freemius' ) ) { // Older SDK version was already loaded. if ( ! $fs_active_plugins->newest->in_activation ) { // Re-order plugins to load this plugin first. fs_newest_sdk_plugin_first(); } // Refresh page. fs_redirect( $_SERVER['REQUEST_URI'] ); } } else { if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $fs_newest_sdk = $fs_active_plugins->newest; $fs_newest_sdk = $fs_active_plugins->plugins[ $fs_newest_sdk->sdk_path ]; $is_newest_sdk_type_theme = ( isset( $fs_newest_sdk->type ) && 'theme' === $fs_newest_sdk->type ); /** * @var bool $is_newest_sdk_module_active * True if the plugin with the newest SDK is active. * True if the newest SDK is part of the current theme or current theme's parent. * False otherwise. */ if ( ! $is_newest_sdk_type_theme ) { $is_newest_sdk_module_active = is_plugin_active( $fs_newest_sdk->plugin_path ); } else { $current_theme = wp_get_theme(); // Detect if current theme is the one registered as newer SDK $is_newest_sdk_module_active = ( strpos( $fs_newest_sdk->plugin_path, '../' . $themes_directory_name . '/' . $current_theme->get_stylesheet() . '/' ) === 0 ); $current_theme_parent = $current_theme->parent(); /** * If the current theme is a child of the theme that has the newest SDK, this prevents a redirects loop * from happening by keeping the SDK info stored in the `fs_active_plugins` option. */ if ( ! $is_newest_sdk_module_active && $current_theme_parent instanceof WP_Theme ) { // Detect if current theme parent is the one registered as newer SDK $is_newest_sdk_module_active = ( strpos( $fs_newest_sdk->plugin_path, '../' . $themes_directory_name . '/' . $current_theme_parent->get_stylesheet() . '/' ) === 0 ); } } if ( $is_current_sdk_newest && ! $is_newest_sdk_module_active && ! $fs_active_plugins->newest->in_activation ) { // If current SDK is the newest and the plugin is NOT active, it means // that the current plugin in activation mode. $fs_active_plugins->newest->in_activation = true; update_option( 'fs_active_plugins', $fs_active_plugins ); } if ( ! $is_theme ) { $sdk_starter_path = fs_normalize_path( WP_PLUGIN_DIR . '/' . $this_sdk_relative_path . '/start.php' ); } else { $sdk_starter_path = fs_normalize_path( $themes_directory . '/' . str_replace( "../{$themes_directory_name}/", '', $this_sdk_relative_path ) . '/start.php' ); } $is_newest_sdk_path_valid = ( $is_newest_sdk_module_active || $fs_active_plugins->newest->in_activation ) && file_exists( $sdk_starter_path ); if ( ! $is_newest_sdk_path_valid && ! $is_current_sdk_newest ) { // Plugin with newest SDK is no longer active, or SDK was moved to a different location. unset( $fs_active_plugins->plugins[ $fs_active_plugins->newest->sdk_path ] ); } if ( ! ( $is_newest_sdk_module_active || $fs_active_plugins->newest->in_activation ) || ! $is_newest_sdk_path_valid || // Is newest SDK downgraded. ( $this_sdk_relative_path == $fs_active_plugins->newest->sdk_path && version_compare( $fs_active_plugins->newest->version, $this_sdk_version, '>' ) ) ) { /** * Plugin with newest SDK is no longer active. * OR * The newest SDK was in the current plugin. BUT, seems like the version of * the SDK was downgraded to a lower SDK. */ // Find the active plugin with the newest SDK version and update the newest reference. fs_fallback_to_newest_active_sdk(); } else { if ( $is_newest_sdk_module_active && $this_sdk_relative_path == $fs_active_plugins->newest->sdk_path && ( $fs_active_plugins->newest->in_activation || ( class_exists( 'Freemius' ) && ( ! defined( 'WP_FS__SDK_VERSION' ) || version_compare( WP_FS__SDK_VERSION, $this_sdk_version, '<' ) ) ) ) ) { if ( $fs_active_plugins->newest->in_activation && ! $is_newest_sdk_type_theme ) { // Plugin no more in activation. $fs_active_plugins->newest->in_activation = false; update_option( 'fs_active_plugins', $fs_active_plugins ); } // Reorder plugins to load plugin with newest SDK first. if ( fs_newest_sdk_plugin_first() ) { // Refresh page after re-order to make sure activated plugin loads newest SDK. if ( class_exists( 'Freemius' ) ) { fs_redirect( $_SERVER['REQUEST_URI'] ); } } } } } if ( class_exists( 'Freemius' ) ) { // SDK was already loaded. return; } if ( isset( $fs_active_plugins->newest ) && version_compare( $this_sdk_version, $fs_active_plugins->newest->version, '<' ) ) { $newest_sdk = $fs_active_plugins->plugins[ $fs_active_plugins->newest->sdk_path ]; $plugins_or_theme_dir_path = ( ! isset( $newest_sdk->type ) || 'theme' !== $newest_sdk->type ) ? WP_PLUGIN_DIR : $themes_directory; $newest_sdk_starter = fs_normalize_path( $plugins_or_theme_dir_path . '/' . str_replace( "../{$themes_directory_name}/", '', $fs_active_plugins->newest->sdk_path ) . '/start.php' ); if ( file_exists( $newest_sdk_starter ) ) { // Reorder plugins to load plugin with newest SDK first. fs_newest_sdk_plugin_first(); // There's a newer SDK version, load it instead of the current one! require_once $newest_sdk_starter; return; } } #endregion SDK Selection Logic -------------------------------------------------------------------- #region Hooks & Filters Collection -------------------------------------------------------------------- /** * Freemius hooks (actions & filters) tags structure: * * fs_{filter/action_name}_{plugin_slug} * * -------------------------------------------------------- * * Usage with WordPress' add_action() / add_filter(): * * add_action('fs_{filter/action_name}_{plugin_slug}', $callable); * * -------------------------------------------------------- * * Usage with Freemius' instance add_action() / add_filter(): * * // No need to add 'fs_' prefix nor '_{plugin_slug}' suffix. * my_freemius()->add_action('{action_name}', $callable); * * -------------------------------------------------------- * * Freemius filters collection: * * fs_connect_url_{plugin_slug} * fs_trial_promotion_message_{plugin_slug} * fs_is_long_term_user_{plugin_slug} * fs_uninstall_reasons_{plugin_slug} * fs_is_plugin_update_{plugin_slug} * fs_api_domains_{plugin_slug} * fs_email_template_sections_{plugin_slug} * fs_support_forum_submenu_{plugin_slug} * fs_support_forum_url_{plugin_slug} * fs_connect_message_{plugin_slug} * fs_connect_message_on_update_{plugin_slug} * fs_uninstall_confirmation_message_{plugin_slug} * fs_pending_activation_message_{plugin_slug} * fs_is_submenu_visible_{plugin_slug} * fs_plugin_icon_{plugin_slug} * fs_show_trial_{plugin_slug} * * -------------------------------------------------------- * * Freemius actions collection: * * fs_after_license_loaded_{plugin_slug} * fs_after_license_change_{plugin_slug} * fs_after_plans_sync_{plugin_slug} * * fs_after_account_details_{plugin_slug} * fs_after_account_user_sync_{plugin_slug} * fs_after_account_plan_sync_{plugin_slug} * fs_before_account_load_{plugin_slug} * fs_after_account_connection_{plugin_slug} * fs_account_property_edit_{plugin_slug} * fs_account_email_verified_{plugin_slug} * fs_account_page_load_before_departure_{plugin_slug} * fs_before_account_delete_{plugin_slug} * fs_after_account_delete_{plugin_slug} * * fs_sdk_version_update_{plugin_slug} * fs_plugin_version_update_{plugin_slug} * * fs_initiated_{plugin_slug} * fs_after_init_plugin_registered_{plugin_slug} * fs_after_init_plugin_anonymous_{plugin_slug} * fs_after_init_plugin_pending_activations_{plugin_slug} * fs_after_init_addon_registered_{plugin_slug} * fs_after_init_addon_anonymous_{plugin_slug} * fs_after_init_addon_pending_activations_{plugin_slug} * * fs_after_premium_version_activation_{plugin_slug} * fs_after_free_version_reactivation_{plugin_slug} * * fs_after_uninstall_{plugin_slug} * fs_before_admin_menu_init_{plugin_slug} */ #endregion Hooks & Filters Collection -------------------------------------------------------------------- if ( ! class_exists( 'Freemius' ) ) { if ( ! defined( 'WP_FS__SDK_VERSION' ) ) { define( 'WP_FS__SDK_VERSION', $this_sdk_version ); } $plugins_or_theme_dir_path = fs_normalize_path( trailingslashit( $is_theme ? $themes_directory : WP_PLUGIN_DIR ) ); if ( 0 === strpos( $file_path, $plugins_or_theme_dir_path ) ) { // No symlinks } else { /** * This logic finds the SDK symlink and set WP_FS__DIR to use it. * * @author Vova Feldman (@svovaf) * @since 1.2.2.5 */ $sdk_symlink = null; // Try to load SDK's symlink from cache. if ( isset( $fs_active_plugins->plugins[ $this_sdk_relative_path ] ) && is_object( $fs_active_plugins->plugins[ $this_sdk_relative_path ] ) && ! empty( $fs_active_plugins->plugins[ $this_sdk_relative_path ]->sdk_symlink ) ) { $sdk_symlink = $fs_active_plugins->plugins[ $this_sdk_relative_path ]->sdk_symlink; if ( 0 === strpos( $sdk_symlink, $plugins_or_theme_dir_path ) ) { /** * Make the symlink path relative. * * @author Leo Fajardo (@leorw) */ $sdk_symlink = substr( $sdk_symlink, strlen( $plugins_or_theme_dir_path ) ); $fs_active_plugins->plugins[ $this_sdk_relative_path ]->sdk_symlink = $sdk_symlink; update_option( 'fs_active_plugins', $fs_active_plugins ); } $realpath = realpath( $plugins_or_theme_dir_path . $sdk_symlink ); if ( ! is_string( $realpath ) || ! file_exists( $realpath ) ) { $sdk_symlink = null; } } if ( empty( $sdk_symlink ) ) // Has symlinks, therefore, we need to configure WP_FS__DIR based on the symlink. { $partial_path_right = basename( $file_path ); $partial_path_left = dirname( $file_path ); $realpath = realpath( $plugins_or_theme_dir_path . $partial_path_right ); while ( '/' !== $partial_path_left && ( false === $realpath || $file_path !== fs_normalize_path( $realpath ) ) ) { $partial_path_right = trailingslashit( basename( $partial_path_left ) ) . $partial_path_right; $partial_path_left_prev = $partial_path_left; $partial_path_left = dirname( $partial_path_left_prev ); /** * Avoid infinite loop if for example `$partial_path_left_prev` is `C:/`, in this case, * `dirname( 'C:/' )` will return `C:/`. * * @author Leo Fajardo (@leorw) */ if ( $partial_path_left === $partial_path_left_prev ) { $partial_path_left = ''; break; } $realpath = realpath( $plugins_or_theme_dir_path . $partial_path_right ); } if ( ! empty( $partial_path_left ) && '/' !== $partial_path_left ) { $sdk_symlink = fs_normalize_path( dirname( $partial_path_right ) ); // Cache value. if ( isset( $fs_active_plugins->plugins[ $this_sdk_relative_path ] ) && is_object( $fs_active_plugins->plugins[ $this_sdk_relative_path ] ) ) { $fs_active_plugins->plugins[ $this_sdk_relative_path ]->sdk_symlink = $sdk_symlink; update_option( 'fs_active_plugins', $fs_active_plugins ); } } } if ( ! empty( $sdk_symlink ) ) { // Set SDK dir to the symlink path. define( 'WP_FS__DIR', $plugins_or_theme_dir_path . $sdk_symlink ); } } // Load SDK files. require_once dirname( __FILE__ ) . '/require.php'; /** * Quick shortcut to get Freemius for specified plugin. * Used by various templates. * * @param number $module_id * * @return Freemius */ function freemius( $module_id ) { return Freemius::instance( $module_id ); } /** * @param string $slug * @param number $plugin_id * @param string $public_key * @param bool $is_live Is live or test plugin. * @param bool $is_premium Hints freemius if running the premium plugin or not. * * @return Freemius * * @deprecated Please use fs_dynamic_init(). */ function fs_init( $slug, $plugin_id, $public_key, $is_live = true, $is_premium = true ) { $fs = Freemius::instance( $plugin_id, $slug, true ); $fs->init( $plugin_id, $public_key, $is_live, $is_premium ); return $fs; } /** * @param array $module Plugin or Theme details. * * @return Freemius * @throws Freemius_Exception */ function fs_dynamic_init( $module ) { $fs = Freemius::instance( $module['id'], $module['slug'], true ); $fs->dynamic_init( $module ); return $fs; } function fs_dump_log() { FS_Logger::dump(); } } freemius/require.php000064400000006667147600046700010573 0ustar00 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: {project} Copyright (C) {year} {fullname} This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .freemius/index.php000064400000000127147600046700010207 0ustar00

    __('Inherit', 'cf7-styler'), 'solid' => __('Solid', 'cf7-styler'), 'dotted' => __('Dotted', 'cf7-styler'), 'dashed' => __('Dashed', 'cf7-styler'), 'double' => __('Double', 'cf7-styler'), 'groove' => __('Groove', 'cf7-styler'), 'ridge' => __('Ridge', 'cf7-styler'), 'inset' => __('Inset', 'cf7-styler'), 'outset' => __('Outset', 'cf7-styler'), ); } }includes/lib/Cf7_Style_Scheme.php000064400000162317147600046700012732 0ustar00 __('Default Scheme', 'cf7-styler'), 'scheme' => array( 'form' => array( 'padding' => array( 'top' => '20', 'right' => '20', 'bottom' => '20', 'left' => '20', ), 'margin' => array( 'top' => '15', 'bottom' => '15', ), 'border' => array( 'width' => array( 'top' => '1', 'right' => '1', 'bottom' => '1', 'left' => '5', ), 'radius' => '10', 'color' => '#1e73be', ), ), 'input' => array( 'full-width' => 'yes', 'text' => array( 'line-height' => '1.6' ), 'bg' => array( 'color' => '#ffffff' ), 'padding' => array( 'top' => '5', 'right' => '10', 'bottom' => '5', 'left' => '10', ), 'border' => array( 'width' => array( 'top' => '1', 'right' => '1', 'bottom' => '1', 'left' => '3', ), 'radius' => '5', 'color' => '#1e73be', ), ), 'button' => array( 'text' => array( 'color' => '#ffffff', 'color-hover' => '#1e73be', 'line-height' => '1.6', ), 'bg' => array( 'color' => '#1e73be', 'color-hover' => '#ffffff', ), 'padding' => 5, 'border' => array( 'width' => '2', 'radius' => '10', 'color' => '#1e73be', 'color-hover' => '#1e73be', ), 'shadow' => array( 'opacity' => '0.5', 'vertical-length' => '5', 'blur-radius' => '5', 'spread-radius' => '-5', 'color' => '#000000', 'position' => 'outline', ) ), ), ); } public static function normalize_style_sheme($style_schemes, $slug) { $style_scheme_settings = !empty($style_schemes[$slug]['scheme']) ? $style_schemes[$slug]['scheme'] : array(); $style_scheme = array(); // General Form Styles $style_scheme['form_text_color'] = !empty($style_scheme_settings["form"]["text"]["color"]) ? $style_scheme_settings["form"]["text"]["color"] : ''; $style_scheme['form_text_label_color'] = !empty($style_scheme_settings["form"]["text"]["label-color"]) ? $style_scheme_settings["form"]["text"]["label-color"] : ''; $style_scheme['form_text_link_color'] = !empty($style_scheme_settings["form"]["text"]["link-color"]) ? $style_scheme_settings["form"]["text"]["link-color"] : ''; $style_scheme['form_text_link_hover_color'] = !empty($style_scheme_settings["form"]["text"]["link-hover-color"]) ? $style_scheme_settings["form"]["text"]["link-hover-color"] : ''; $style_scheme['form_text_font_family'] = !empty($style_scheme_settings["form"]["text"]["font-family"]) ? $style_scheme_settings["form"]["text"]["font-family"] : ''; $style_scheme['form_bg_color'] = !empty($style_scheme_settings["form"]["bg"]["color"]) ? $style_scheme_settings["form"]["bg"]["color"]: ''; $style_scheme['form_bg_img'] = !empty($style_scheme_settings["form"]["bg"]["img"]) ? $style_scheme_settings["form"]["bg"]["img"] : ''; $style_scheme['form_bg_img_opacity'] = !empty($style_scheme_settings["form"]["bg"]["img-opacity"]) ? $style_scheme_settings["form"]["bg"]["img-opacity"] : ''; $style_scheme['form_bg_img_size'] = !empty($style_scheme_settings["form"]["bg"]["img-size"]) ? $style_scheme_settings["form"]["bg"]["img-size"] : ''; $style_scheme['form_bg_img_position'] = !empty($style_scheme_settings["form"]["bg"]["img-position"]) ? $style_scheme_settings["form"]["bg"]["img-position"] : ''; $style_scheme['form_text_size'] = !empty($style_scheme_settings["form"]["text"]["size"]) ? $style_scheme_settings["form"]["text"]["size"] : ''; $style_scheme['form_text_label_size'] = !empty($style_scheme_settings["form"]["text"]["label-size"]) ? $style_scheme_settings["form"]["text"]["label-size"] : ''; $style_scheme['form_text_label_weight'] = !empty($style_scheme_settings["form"]["text"]["label-weight"]) ? $style_scheme_settings["form"]["text"]["label-weight"] : ''; $style_scheme['form_text_label_style'] = !empty($style_scheme_settings["form"]["text"]["label-style"]) ? $style_scheme_settings["form"]["text"]["label-style"] : ''; $style_scheme['form_padding'] = !empty($style_scheme_settings["form"]["padding"]) && !is_array($style_scheme_settings["form"]["padding"]) ? $style_scheme_settings["form"]["padding"] : ''; $style_scheme['form_padding_top'] = !empty($style_scheme_settings["form"]["padding"]['top']) ? $style_scheme_settings["form"]["padding"]['top'] : ''; $style_scheme['form_padding_right'] = !empty($style_scheme_settings["form"]["padding"]['right']) ? $style_scheme_settings["form"]["padding"]['right'] : ''; $style_scheme['form_padding_bottom'] = !empty($style_scheme_settings["form"]["padding"]['bottom']) ? $style_scheme_settings["form"]["padding"]['bottom'] : ''; $style_scheme['form_padding_left'] = !empty($style_scheme_settings["form"]["padding"]['left']) ? $style_scheme_settings["form"]["padding"]['left'] : ''; $style_scheme['form_margin'] = !empty($style_scheme_settings["form"]["margin"]) && !is_array($style_scheme_settings["form"]["margin"]) ? $style_scheme_settings["form"]["margin"] : ''; $style_scheme['form_margin_top'] = !empty($style_scheme_settings["form"]["margin"]['top']) ? $style_scheme_settings["form"]["margin"]['top'] : ''; $style_scheme['form_margin_right'] = !empty($style_scheme_settings["form"]["margin"]['right']) ? $style_scheme_settings["form"]["margin"]['right'] : ''; $style_scheme['form_margin_bottom'] = !empty($style_scheme_settings["form"]["margin"]['bottom']) ? $style_scheme_settings["form"]["margin"]['bottom'] : ''; $style_scheme['form_margin_left'] = !empty($style_scheme_settings["form"]["margin"]['left']) ? $style_scheme_settings["form"]["margin"]['left'] : ''; $style_scheme['form_border_width'] = !empty($style_scheme_settings["form"]["border"]["width"]) && !is_array($style_scheme_settings["form"]["border"]["width"]) ? $style_scheme_settings["form"]["border"]["width"] : ''; $style_scheme['form_border_width_top'] = !empty($style_scheme_settings["form"]["border"]["width"]['top']) ? $style_scheme_settings["form"]["border"]["width"]['top'] : ''; $style_scheme['form_border_width_right'] = !empty($style_scheme_settings["form"]["border"]["width"]['right']) ? $style_scheme_settings["form"]["border"]["width"]['right'] : ''; $style_scheme['form_border_width_bottom'] = !empty($style_scheme_settings["form"]["border"]["width"]['bottom']) ? $style_scheme_settings["form"]["border"]["width"]['bottom'] : ''; $style_scheme['form_border_width_left'] = !empty($style_scheme_settings["form"]["border"]["width"]['left']) ? $style_scheme_settings["form"]["border"]["width"]['left'] : ''; $style_scheme['form_border_type'] = !empty($style_scheme_settings["form"]["border"]["type"]) ? $style_scheme_settings["form"]["border"]["type"] : ''; $style_scheme['form_border_radius'] = !empty($style_scheme_settings["form"]["border"]["radius"]) ? $style_scheme_settings["form"]["border"]["radius"] : ''; $style_scheme['form_border_color'] = !empty($style_scheme_settings["form"]["border"]["color"]) ? $style_scheme_settings["form"]["border"]["color"] : ''; $style_scheme['form_border_shadow_horizontal_length'] = !empty($style_scheme_settings["form"]["shadow"]["horizontal-length"]) ? $style_scheme_settings["form"]["shadow"]["horizontal-length"] : ''; $style_scheme['form_border_shadow_vertical_length'] = !empty($style_scheme_settings["form"]["shadow"]["vertical-length"]) ? $style_scheme_settings["form"]["shadow"]["vertical-length"] : ''; $style_scheme['form_border_shadow_blur_radius'] = !empty($style_scheme_settings["form"]["shadow"]["blur-radius"]) ? $style_scheme_settings["form"]["shadow"]["blur-radius"] : ''; $style_scheme['form_border_shadow_spread_radius'] = !empty($style_scheme_settings["form"]["shadow"]["spread-radius"]) ? $style_scheme_settings["form"]["shadow"]["spread-radius"] : ''; $style_scheme['form_border_shadow_color'] = !empty($style_scheme_settings["form"]["shadow"]["color"]) ? $style_scheme_settings["form"]["shadow"]["color"] : ''; $style_scheme['form_border_shadow_opacity'] = !empty($style_scheme_settings["form"]["shadow"]["opacity"]) ? $style_scheme_settings["form"]["shadow"]["opacity"] : ''; $style_scheme['form_border_shadow_position'] = !empty($style_scheme_settings["form"]["shadow"]["position"]) ? $style_scheme_settings["form"]["shadow"]["position"] : ''; $style_scheme['input_full_width'] = !empty($style_scheme_settings["input"]["full-width"]) ? $style_scheme_settings["input"]["full-width"] : ''; $style_scheme['input_text_color'] = !empty($style_scheme_settings["input"]["text"]["color"]) ? $style_scheme_settings["input"]["text"]["color"] : ''; $style_scheme['input_bg_color'] = !empty($style_scheme_settings["input"]["bg"]["color"]) ? $style_scheme_settings["input"]["bg"]["color"] : ''; $style_scheme['input_bg_color_opacity'] = !empty($style_scheme_settings["input"]["bg"]["color-opacity"]) ? $style_scheme_settings["input"]["bg"]["color-opacity"] : ''; $style_scheme['input_padding'] = !empty($style_scheme_settings["input"]["padding"]) && !is_array($style_scheme_settings["input"]["padding"]) ? $style_scheme_settings["input"]["padding"] : ''; $style_scheme['input_padding_top'] = !empty($style_scheme_settings["input"]["padding"]['top']) ? $style_scheme_settings["input"]["padding"]['top'] : ''; $style_scheme['input_padding_right'] = !empty($style_scheme_settings["input"]["padding"]['right']) ? $style_scheme_settings["input"]["padding"]['right'] : ''; $style_scheme['input_padding_bottom'] = !empty($style_scheme_settings["input"]["padding"]['bottom']) ? $style_scheme_settings["input"]["padding"]['bottom'] : ''; $style_scheme['input_padding_left'] = !empty($style_scheme_settings["input"]["padding"]['left']) ? $style_scheme_settings["input"]["padding"]['left'] : ''; $style_scheme['input_margin'] = !empty($style_scheme_settings["input"]["margin"]) && !is_array($style_scheme_settings["input"]["margin"]) ? $style_scheme_settings["input"]["margin"] : ''; $style_scheme['input_margin_top'] = !empty($style_scheme_settings["input"]["margin"]['top']) ? $style_scheme_settings["input"]["margin"]['top'] : ''; $style_scheme['input_margin_right'] = !empty($style_scheme_settings["input"]["margin"]['right']) ? $style_scheme_settings["input"]["margin"]['right'] : ''; $style_scheme['input_margin_bottom'] = !empty($style_scheme_settings["input"]["margin"]['bottom']) ? $style_scheme_settings["input"]["margin"]['bottom'] : ''; $style_scheme['input_margin_left'] = !empty($style_scheme_settings["input"]["margin"]['left']) ? $style_scheme_settings["input"]["margin"]['left'] : ''; $style_scheme['input_border_width'] = !empty($style_scheme_settings["input"]["border"]["width"]) && !is_array($style_scheme_settings["input"]["border"]["width"]) ? $style_scheme_settings["input"]["border"]["width"] : ''; $style_scheme['input_border_width_top'] = !empty($style_scheme_settings["input"]["border"]["width"]['top']) ? $style_scheme_settings["input"]["border"]["width"]['top'] : ''; $style_scheme['input_border_width_right'] = !empty($style_scheme_settings["input"]["border"]["width"]['right']) ? $style_scheme_settings["input"]["border"]["width"]['right'] : ''; $style_scheme['input_border_width_bottom'] = !empty($style_scheme_settings["input"]["border"]["width"]['bottom']) ? $style_scheme_settings["input"]["border"]["width"]['bottom'] : ''; $style_scheme['input_border_width_left'] = !empty($style_scheme_settings["input"]["border"]["width"]['left']) ? $style_scheme_settings["input"]["border"]["width"]['left'] : ''; $style_scheme['input_text_size'] = !empty($style_scheme_settings["input"]["text"]["size"]) ? $style_scheme_settings["input"]["text"]["size"] : ''; $style_scheme['input_text_line_height'] = !empty($style_scheme_settings["input"]["text"]["line-height"]) ? $style_scheme_settings["input"]["text"]["line-height"] : ''; $style_scheme['input_border_radius'] = !empty($style_scheme_settings["input"]["border"]["radius"]) ? $style_scheme_settings["input"]["border"]["radius"] : ''; $style_scheme['input_border_color'] = !empty($style_scheme_settings["input"]["border"]["color"]) ? $style_scheme_settings["input"]["border"]["color"] : ''; $style_scheme['input_border_shadow_horizontal_length'] = !empty($style_scheme_settings["input"]["shadow"]["horizontal-length"]) ? $style_scheme_settings["input"]["shadow"]["horizontal-length"] : ''; $style_scheme['input_border_shadow_vertical_length'] = !empty($style_scheme_settings["input"]["shadow"]["vertical-length"]) ? $style_scheme_settings["input"]["shadow"]["vertical-length"] : ''; $style_scheme['input_border_shadow_blur_radius'] = !empty($style_scheme_settings["input"]["shadow"]["blur-radius"]) ? $style_scheme_settings["input"]["shadow"]["blur-radius"] : ''; $style_scheme['input_border_shadow_spread_radius'] = !empty($style_scheme_settings["input"]["shadow"]["spread-radius"]) ? $style_scheme_settings["input"]["shadow"]["spread-radius"] : ''; $style_scheme['input_border_shadow_color'] = !empty($style_scheme_settings["input"]["shadow"]["color"]) ? $style_scheme_settings["input"]["shadow"]["color"] : ''; $style_scheme['input_border_shadow_opacity'] = !empty($style_scheme_settings["input"]["shadow"]["opacity"]) ? $style_scheme_settings["input"]["shadow"]["opacity"] : ''; $style_scheme['input_border_shadow_position'] = !empty($style_scheme_settings["input"]["shadow"]["position"]) ? $style_scheme_settings["input"]["shadow"]["position"] : ''; $style_scheme['checkbox_full_width'] = !empty($style_scheme_settings["checkbox"]["full-width"]) ? $style_scheme_settings["checkbox"]["full-width"] : ''; $style_scheme['radiobutton_full_width'] = !empty($style_scheme_settings["radiobutton"]["full-width"]) ? $style_scheme_settings["radiobutton"]["full-width"] : ''; $style_scheme['checkbox_text_label_size'] = !empty($style_scheme_settings["checkbox"]["text"]["label-size"]) ? $style_scheme_settings["checkbox"]["text"]["label-size"] : ''; $style_scheme['button_full_width'] = !empty($style_scheme_settings["button"]["full-width"]) ? $style_scheme_settings["button"]["full-width"] : ''; $style_scheme['button_text_color'] = !empty($style_scheme_settings["button"]["text"]["color"]) ? $style_scheme_settings["button"]["text"]["color"] : ''; $style_scheme['button_bg_color'] = !empty($style_scheme_settings["button"]["bg"]["color"]) ? $style_scheme_settings["button"]["bg"]["color"] : ''; $style_scheme['button_text_color_hover'] = !empty($style_scheme_settings["button"]["text"]["color-hover"]) ? $style_scheme_settings["button"]["text"]["color-hover"] : ''; $style_scheme['button_bg_color_hover'] = !empty($style_scheme_settings["button"]["bg"]["color-hover"]) ? $style_scheme_settings["button"]["bg"]["color-hover"] : ''; $style_scheme['button_text_size'] = !empty($style_scheme_settings["button"]["text"]["size"]) ? $style_scheme_settings["button"]["text"]["size"] : ''; $style_scheme['button_text_line_height'] = !empty($style_scheme_settings["button"]["text"]["line-height"]) ? $style_scheme_settings["button"]["text"]["line-height"] : ''; $style_scheme['button_padding'] = !empty($style_scheme_settings["button"]["padding"]) ? $style_scheme_settings["button"]["padding"] : ''; $style_scheme['button_border_width'] = !empty($style_scheme_settings["button"]["border"]["width"]) ? $style_scheme_settings["button"]["border"]["width"] : ''; $style_scheme['button_border_radius'] = !empty($style_scheme_settings["button"]["border"]["radius"]) ? $style_scheme_settings["button"]["border"]["radius"] : ''; $style_scheme['button_border_color'] = !empty($style_scheme_settings["button"]["border"]["color"]) ? $style_scheme_settings["button"]["border"]["color"] : ''; $style_scheme['button_border_color_hover'] = !empty($style_scheme_settings["button"]["border"]["color-hover"]) ? $style_scheme_settings["button"]["border"]["color-hover"] : ''; $style_scheme['button_border_shadow_horizontal_length'] = !empty($style_scheme_settings["button"]["shadow"]["horizontal-length"]) ? $style_scheme_settings["button"]["shadow"]["horizontal-length"] : ''; $style_scheme['button_border_shadow_vertical_length'] = !empty($style_scheme_settings["button"]["shadow"]["vertical-length"]) ? $style_scheme_settings["button"]["shadow"]["vertical-length"] : ''; $style_scheme['button_border_shadow_blur_radius'] = !empty($style_scheme_settings["button"]["shadow"]["blur-radius"]) ? $style_scheme_settings["button"]["shadow"]["blur-radius"] : ''; $style_scheme['button_border_shadow_spread_radius'] = !empty($style_scheme_settings["button"]["shadow"]["spread-radius"]) ? $style_scheme_settings["button"]["shadow"]["spread-radius"] : ''; $style_scheme['button_border_shadow_color'] = !empty($style_scheme_settings["button"]["shadow"]["color"]) ? $style_scheme_settings["button"]["shadow"]["color"] : ''; $style_scheme['button_border_shadow_opacity'] = !empty($style_scheme_settings["button"]["shadow"]["opacity"]) ? $style_scheme_settings["button"]["shadow"]["opacity"] : ''; $style_scheme['button_border_shadow_position'] = !empty($style_scheme_settings["button"]["shadow"]["position"]) ? $style_scheme_settings["button"]["shadow"]["position"] : ''; $style_scheme['custom_css'] = !empty($style_scheme_settings["custom"]["css"]) ? $style_scheme_settings["custom"]["css"] : ''; return $style_scheme; } public static function get_inline_style_scheme($style_schemes, $slug, $form_id = array(), $form_excluded = array(), $form_prefix = '') { $style_scheme = self::normalize_style_sheme($style_schemes, $slug); $web_safe_fonts = Cf7_Style_Scheme::get_web_safe_fonts(); $wrappers = array(); if (!empty($form_excluded)) { foreach ($form_excluded as $form_excluded_id => $form_excluded_style) { $wrappers[] = $form_prefix . '#cf7cstmzr-form:not(.cf7cstmzr-form-'.$form_excluded_id.') '; $form = get_post($form_excluded_id); if ($form && get_post_type( $form ) === 'wpcf7_contact_form') { $hash = substr(get_post_meta( $form_excluded_id, '_hash', true ), 0, 7 ); if (!empty($hash)) $form_excluded_id = $hash; $wrappers[] = $form_prefix . '#cf7cstmzr-form:not(.cf7cstmzr-form-'.$form_excluded_id.') '; } } } elseif (!empty($form_id)) { foreach ($form_id as $item) { $wrappers[] = $form_prefix . '#cf7cstmzr-form.cf7cstmzr-form-'.$item.' '; $form = get_post($item); if ($form && get_post_type( $form ) === 'wpcf7_contact_form') { $hash = substr(get_post_meta( $item, '_hash', true ), 0, 7 ); if (!empty($hash)) $item = $hash; $wrappers[] = $form_prefix . '#cf7cstmzr-form.cf7cstmzr-form-'.$item.' '; } } } else { $wrappers[] = $form_prefix . '#cf7cstmzr-form '; } ob_start(); ?> ', '', $style); $style = str_replace('', '', $style); return $style; } public static function form_preview($id) { ?>
    -1, 'orderby' => 'title', 'order' => 'ASC', 'post_type' => 'wpcf7_contact_form', 'post_status' => 'publish', 'suppress_filters' => false, // подавление работы фильтров изменения SQL запроса 'meta_query' => array( 'relation' => 'EXISTS', array( 'key' => 'cf7cstmzr_style_scheme', ) ) ); return get_posts($cf7_scheme_args); } public static function get_forms_group_by_style_scheme() { $style_schemes = get_option('cf7cstmzr_style_schemes', array()); $forms_group_by_style_scheme = array(); $forms_with_style_schemes = self::get_forms_with_style_schemes(); if (!empty($forms_with_style_schemes)) { foreach ($forms_with_style_schemes as $form) { $cf7cstmzr_style_scheme = get_post_meta( $form->ID, 'cf7cstmzr_style_scheme', true ); if (empty($style_schemes[$cf7cstmzr_style_scheme])) { delete_post_meta( $form->ID, 'cf7cstmzr_style_scheme' ); } else { if (!empty($cf7cstmzr_style_scheme)) { $forms_group_by_style_scheme[$cf7cstmzr_style_scheme][] = $form->ID; } } } } return $forms_group_by_style_scheme; } public static function get_web_safe_fonts() { return array( 'georgia' => array ( 'Georgia', 'Georgia, serif' ), 'palatino' => array ( 'Palatino Linotype', '"Palatino Linotype", "Book Antiqua", Palatino, serif' ), 'times' => array ( 'Times New Roman', '"Times New Roman", Times, serif' ), 'arial' => array ( 'Arial', 'Arial, Helvetica, sans-serif' ), 'tahoma' => array ( 'Tahoma', 'Tahoma, Geneva, sans-serif' ), 'verdana' => array ( 'Verdana', 'Verdana, Geneva, sans-serif' ), ); } public static function show_shadow_css_rules($horizontal_length, $vertical_length, $blur_radius, $spread_radius, $opacity, $color, $position) { $default_horizontal_length = 0; $default_vertical_length = 0; $default_blur_radius = 0; $default_spread_radius = 0; $default_opacity = 0; $default_color = '#000000'; $default_position = ''; if (!empty($horizontal_length)) $default_horizontal_length = $horizontal_length; if (!empty($vertical_length)) $default_vertical_length = $vertical_length; if (!empty($blur_radius)) $default_blur_radius = $blur_radius; if (!empty($spread_radius)) $default_spread_radius = $spread_radius; if (!empty($opacity)) $default_opacity = $opacity; if (!empty($position) && 'inset' === $position) $default_position = 'inset '; if (!empty($color)) { $default_color = $color; } else { $default_opacity = 0; } list($r, $g, $b) = sscanf($default_color, "#%02x%02x%02x"); $default_color = $r.','.$g.','.$b.','; $rules = array ( '-webkit-box-shadow', '-moz-box-shadow', 'box-shadow', ); foreach ($rules as $rule) { ?> : px px px px rgba(); -1, 'orderby' => 'title', 'order' => 'ASC', 'post_type' => 'wpcf7_contact_form', 'post_status' => 'publish', 'suppress_filters' => false, // подавление работы фильтров изменения SQL запроса 'meta_query' => array( 'relation' => 'EXISTS', array( 'key' => 'cf7cstmzr_style_scheme', ) ) ); $cf7_forms = get_posts($args); if (!empty($cf7_forms)) { foreach ($cf7_forms as $cf_7_form) { $cf7cstmzr_style_scheme = get_post_meta( $cf_7_form->ID, 'cf7cstmzr_style_scheme', true ); $forms[$cf_7_form->ID] = $cf7cstmzr_style_scheme; } } } else { $args = array ( 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC', 'post_type' => 'wpcf7_contact_form', 'post_status' => 'publish', 'suppress_filters' => false, // подавление работы фильтров изменения SQL запроса 'meta_query' => array( 'relation' => 'EXISTS', array( 'key' => 'cf7cstmzr_style_scheme', ) ) ); $cf7_forms = get_posts($args); if (!empty($cf7_forms)) { $i = 1; $styled_scheme = false; foreach ($cf7_forms as $cf_7_form) { if (!$styled_scheme) { $cf7cstmzr_style_scheme = get_post_meta( $cf_7_form->ID, 'cf7cstmzr_style_scheme', true ); if ('default' !== $cf7cstmzr_style_scheme) { delete_post_meta( $cf_7_form->ID, 'cf7cstmzr_style_scheme' ); } else { $forms[$cf_7_form->ID] = $cf7cstmzr_style_scheme; $styled_scheme = true; } } else { delete_post_meta( $cf_7_form->ID, 'cf7cstmzr_style_scheme' ); } } } } return $forms; } public static function get_globally_styled_forms() { $forms = array(); $args = array ( 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC', 'post_type' => 'wpcf7_contact_form', 'post_status' => 'publish', 'suppress_filters' => false, // подавление работы фильтров изменения SQL запроса ); $cf7_forms = get_posts($args); if (!empty($cf7_forms)) { foreach ($cf7_forms as $cf_7_form) { $forms[$cf_7_form->ID] = 'default'; } } if (!empty($forms)) { $individual_forms = self::get_individually_styled_forms(); if (!empty($individual_forms)) { foreach ($individual_forms as $individual_form_id => $individual_form_style) { unset($forms[$individual_form_id]); } } } return $forms; } public static function style_selectors($selectors, $wrappers) { $selectors_wrapper = array(); foreach ($selectors as $selector) { foreach ($wrappers as $wrapper) { $selectors_wrapper[] = $wrapper . $selector; } } echo implode( ',' . PHP_EOL, $selectors_wrapper ); } } includes/lib/Cf7_Required_Plugin.php000064400000007152147600046700013437 0ustar00 array( 'label' => 'Contact Form 7', 'link' => 'https://wordpress.org/plugins/contact-form-7/', 'zip_path' => 'https://downloads.wordpress.org/plugin/contact-form-7.5.1.4.zip', 'slug' => 'contact-form-7/wp-contact-form-7.php', 'author' => __('By Takayuki Miyoshi', 'cf7-styler'), 'description' => '' ), ); if( !function_exists('is_plugin_active') ) { include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); } foreach ($required_plugins as $slug => $data) { if (is_plugin_active($data['slug'])) { unset($required_plugins[$slug]); } } return $required_plugins; } public static function is_plugin_installed( $plugin_slug ) { if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all_plugins = get_plugins(); if ( !empty( $all_plugins[$plugin_slug] ) ) { return true; } else { return false; } } public static function install_plugin( $plugin_zip ) { include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; wp_cache_flush(); $upgrader = new Plugin_Upgrader(new Cf7_Quiet_Skin()); $installed = $upgrader->install( $plugin_zip ); return $installed; } public static function upgrade_plugin( $plugin_slug ) { include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; wp_cache_flush(); $upgrader = new Plugin_Upgrader(new Cf7_Quiet_Skin()); $upgraded = $upgrader->upgrade( $plugin_slug ); return $upgraded; } public static function is_plugin_newest_version($plugin_slug) { $current = get_site_transient( 'update_plugins' ); if ( ! isset( $current->response[ $plugin_slug ] ) ) { return true; } return false; } public static function install_and_activate_plugin($plugin) { $required_plugins = self::get_required_plugins(); $plugin_data = $required_plugins[$plugin]; $plugin_zip = $plugin_data['zip_path']; $plugin_slug = $plugin_data['slug']; if (!self::is_plugin_installed($plugin_slug)) { $installed = self::install_plugin($plugin_zip); if (!is_wp_error( $installed ) && $installed) { self::upgrade_plugin( $plugin_slug ); $installed = true; } } elseif(self::is_plugin_newest_version($plugin_slug)) { $installed = true; } else { self::upgrade_plugin( $plugin_slug ); $installed = true; } if ( !is_wp_error( $installed ) && $installed ) { $activate = activate_plugin( $plugin_slug ); if ( is_null($activate) ) { return true; } else { return false; } } else { return false; } } } if (!class_exists('WP_Upgrader_Skin')) { include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php'; } if(version_compare(get_bloginfo('version'),'5.3', '>=') ) { class Cf7_Quiet_Skin extends WP_Upgrader_Skin { public function feedback($string, ...$args) {} } } else { class Cf7_Quiet_Skin extends WP_Upgrader_Skin { public function feedback($string) {} } } includes/lib/Cf7_License.php000064400000001617147600046700011723 0ustar00is_paying_or_trial(); if ($is_paying) { return 'pro'; } if (cf7_styler()->is_paying__fs__()) { return 'pro'; } if (cf7_styler()->is_trial()) { return 'pro'; } } if ( !cf7cstmzr_is_plugin_activated( 'wp2leads', 'wp2leads.php' ) ) { return 'free'; } $wp2l_license = get_option('wp2l_license'); $wp2l_license_version = !empty($wp2l_license['version']) ? $wp2l_license['version'] : 'free'; return $wp2l_license_version; } public static function is_wp2leads_installed() { return cf7cstmzr_is_plugin_activated( 'wp2leads', 'wp2leads.php' ); } }includes/index.php000064400000000032147600046700010171 0ustar00 */ class Cf7_Customizer { /** * The loader that's responsible for maintaining and registering all hooks that power * the plugin. * * @since 1.0.0 * @access protected * @var Cf7_Customizer_Loader $loader Maintains and registers all hooks for the plugin. */ protected $loader; /** * The unique identifier of this plugin. * * @since 1.0.0 * @access protected * @var string $plugin_name The string used to uniquely identify this plugin. */ protected $plugin_name; /** * The current version of the plugin. * * @since 1.0.0 * @access protected * @var string $version The current version of the plugin. */ protected $version; /** * Define the core functionality of the plugin. * * Set the plugin name and the plugin version that can be used throughout the plugin. * Load the dependencies, define the locale, and set the hooks for the admin area and * the public-facing side of the site. * * @since 1.0.0 */ public function __construct() { if ( defined( 'CF7_CUSTOMIZER_VERSION' ) ) { $this->version = CF7_CUSTOMIZER_VERSION; } else { $this->version = '1.0.0'; } $this->plugin_name = 'cf7-styler'; $this->load_dependencies(); $this->set_locale(); $this->define_admin_hooks(); $this->define_public_hooks(); } /** * Load the required dependencies for this plugin. * * Include the following files that make up the plugin: * * - Cf7_Customizer_Loader. Orchestrates the hooks of the plugin. * - Cf7_Customizer_i18n. Defines internationalization functionality. * - Cf7_Customizer_Admin. Defines all hooks for the admin area. * - Cf7_Customizer_Public. Defines all hooks for the public side of the site. * * Create an instance of the loader which will be used to register the hooks * with WordPress. * * @since 1.0.0 * @access private */ private function load_dependencies() { /** * The class responsible for orchestrating the actions and filters of the * core plugin. */ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-cf7-customizer-loader.php'; /** * The class responsible for defining internationalization functionality * of the plugin. */ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-cf7-customizer-i18n.php'; /** * Libraries */ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/lib/Cf7_Required_Plugin.php'; require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/lib/Cf7_Style_Scheme.php'; require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/lib/Cf7_License.php'; require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/lib/Cf7_Template.php'; /** * The class responsible for defining all actions that occur in the admin area. */ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-cf7-customizer-admin.php'; require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-cf7-customizer-admin-ajax.php'; /** * The class responsible for defining all actions that occur in the public-facing * side of the site. */ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-cf7-customizer-public.php'; $this->loader = new Cf7_Customizer_Loader(); } /** * Define the locale for this plugin for internationalization. * * Uses the Cf7_Customizer_i18n class in order to set the domain and to register the hook * with WordPress. * * @since 1.0.0 * @access private */ private function set_locale() { $plugin_i18n = new Cf7_Customizer_i18n(); $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' ); } /** * Register all of the hooks related to the admin area functionality * of the plugin. * * @since 1.0.0 * @access private */ private function define_admin_hooks() { $plugin_admin = new Cf7_Customizer_Admin( $this->plugin_name, $this->version ); $plugin_admin_ajax = new Cf7_Customizer_Admin_Ajax( $this->plugin_name, $this->version ); $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' ); $this->loader->add_action( 'admin_menu', $plugin_admin, 'settings_page' ); $this->loader->add_action( 'admin_menu', $plugin_admin, 'plugin_menu_optin', 50 ); $this->loader->add_action( 'init', $plugin_admin, 'rewrite_rule' ); $this->loader->add_action( 'init', $plugin_admin, 'check_installation' ); $this->loader->add_action( 'init', $plugin_admin, 'check_version' ); $this->loader->add_action( 'template_redirect', $plugin_admin, 'template_redirect' ); $this->loader->add_filter('show_admin_bar', $plugin_admin, 'show_admin_bar'); $this->loader->add_filter('admin_body_class', $plugin_admin, 'admin_body_class'); $this->loader->add_action('wp_ajax_cf7cstmzr_save_form_customizer_settings', $plugin_admin_ajax, 'save_form_customizer_settings'); $this->loader->add_action('wp_ajax_cf7cstmzr_new_form_customizer_settings', $plugin_admin_ajax, 'new_form_customizer_settings'); $this->loader->add_action('wp_ajax_cf7cstmzr_delete_form_customizer_settings', $plugin_admin_ajax, 'delete_form_customizer_settings'); $this->loader->add_action('wp_ajax_cf7cstmzr_enable_globally', $plugin_admin_ajax, 'enable_globally'); $this->loader->add_action('wp_ajax_cf7cstmzr_disable_globally', $plugin_admin_ajax, 'disable_globally'); $this->loader->add_action('wp_ajax_cf7cstmzr_enable_for_form', $plugin_admin_ajax, 'enable_for_form'); $this->loader->add_action('wp_ajax_cf7cstmzr_disable_for_form', $plugin_admin_ajax, 'disable_for_form'); $this->loader->add_action('wp_ajax_cf7cstmzr_change_form_preview', $plugin_admin_ajax, 'change_form_preview'); $this->loader->add_action('wp_ajax_cf7cstmzr_preview_form_customizer_settings', $plugin_admin_ajax, 'preview_form_customizer_settings'); $this->loader->add_action('wp_ajax_cf7cstmzr_load_body_tag', $plugin_admin_ajax, 'load_body_tag'); $this->loader->add_action('wp_ajax_cf7cstmzr_cache_form', $plugin_admin_ajax, 'cache_form'); $this->loader->add_action('wp_ajax_nopriv_cf7cstmzr_cache_form', $plugin_admin_ajax, 'cache_form'); $this->loader->add_action('wp_ajax_cf7cstmzr_install_plugin', $plugin_admin_ajax, 'install_plugin'); $this->loader->add_action('wp_ajax_cf7cstmzr_close_welcome', $plugin_admin_ajax, 'close_welcome'); $this->loader->add_filter('wpcf7_editor_panels', $plugin_admin, 'add_cf7_metabox'); $this->loader->add_action('save_post_wpcf7_contact_form', $plugin_admin, 'save_cf7_metabox', 15, 3); } /** * Register all of the hooks related to the public-facing functionality * of the plugin. * * @since 1.0.0 * @access private */ private function define_public_hooks() { $plugin_public = new Cf7_Customizer_Public( $this->get_plugin_name(), $this->get_version() ); $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' ); $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' ); $this->loader->add_filter( 'do_shortcode_tag', $plugin_public, 'add_wrapper', 9999, 4 ); $this->loader->add_action('wp_ajax_cf7cstmzr_frontend_save', $plugin_public, 'save_frontend_settings'); $this->loader->add_action('wp_ajax_nopriv_cf7cstmzr_frontend_save', $plugin_public, 'save_frontend_settings'); } /** * Run the loader to execute all of the hooks with WordPress. * * @since 1.0.0 */ public function run() { $this->loader->run(); } /** * The name of the plugin used to uniquely identify it within the context of * WordPress and to define internationalization functionality. * * @since 1.0.0 * @return string The name of the plugin. */ public function get_plugin_name() { return $this->plugin_name; } /** * The reference to the class that orchestrates the hooks with the plugin. * * @since 1.0.0 * @return Cf7_Customizer_Loader Orchestrates the hooks of the plugin. */ public function get_loader() { return $this->loader; } /** * Retrieve the version number of the plugin. * * @since 1.0.0 * @return string The version number of the plugin. */ public function get_version() { return $this->version; } } includes/class-cf7-customizer-loader.php000064400000011411147600046700014315 0ustar00 */ class Cf7_Customizer_Loader { /** * The array of actions registered with WordPress. * * @since 1.0.0 * @access protected * @var array $actions The actions registered with WordPress to fire when the plugin loads. */ protected $actions; /** * The array of filters registered with WordPress. * * @since 1.0.0 * @access protected * @var array $filters The filters registered with WordPress to fire when the plugin loads. */ protected $filters; /** * Initialize the collections used to maintain the actions and filters. * * @since 1.0.0 */ public function __construct() { $this->actions = array(); $this->filters = array(); } /** * Add a new action to the collection to be registered with WordPress. * * @since 1.0.0 * @param string $hook The name of the WordPress action that is being registered. * @param object $component A reference to the instance of the object on which the action is defined. * @param string $callback The name of the function definition on the $component. * @param int $priority Optional. The priority at which the function should be fired. Default is 10. * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. */ public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); } /** * Add a new filter to the collection to be registered with WordPress. * * @since 1.0.0 * @param string $hook The name of the WordPress filter that is being registered. * @param object $component A reference to the instance of the object on which the filter is defined. * @param string $callback The name of the function definition on the $component. * @param int $priority Optional. The priority at which the function should be fired. Default is 10. * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1 */ public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); } /** * A utility function that is used to register the actions and hooks into a single * collection. * * @since 1.0.0 * @access private * @param array $hooks The collection of hooks that is being registered (that is, actions or filters). * @param string $hook The name of the WordPress filter that is being registered. * @param object $component A reference to the instance of the object on which the filter is defined. * @param string $callback The name of the function definition on the $component. * @param int $priority The priority at which the function should be fired. * @param int $accepted_args The number of arguments that should be passed to the $callback. * @return array The collection of actions and filters registered with WordPress. */ private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { $hooks[] = array( 'hook' => $hook, 'component' => $component, 'callback' => $callback, 'priority' => $priority, 'accepted_args' => $accepted_args ); return $hooks; } /** * Register the filters and actions with WordPress. * * @since 1.0.0 */ public function run() { foreach ( $this->filters as $hook ) { add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); } foreach ( $this->actions as $hook ) { add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); } } } includes/class-cf7-customizer-i18n.php000064400000002624147600046700013634 0ustar00 */ class Cf7_Customizer_i18n { /** * Load the plugin text domain for translation. * * @since 1.0.0 */ public function load_plugin_textdomain() { add_filter( 'plugin_locale', 'Cf7_Customizer_i18n::check_de_locale'); load_plugin_textdomain( 'cf7-styler', false, dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/' ); remove_filter( 'plugin_locale', 'Cf7_Customizer_i18n::check_de_locale'); } public static function check_de_locale($domain) { $site_lang = get_user_locale(); $de_lang_list = array( 'de_CH_informal', 'de_DE_formal', 'de_AT', 'de_CH', 'de_DE' ); if (in_array($site_lang, $de_lang_list)) return 'de_DE'; return $domain; } } includes/class-cf7-customizer-deactivator.php000064400000001166147600046700015362 0ustar00 */ class Cf7_Customizer_Deactivator { /** * Short Description. (use period) * * Long Description. * * @since 1.0.0 */ public static function deactivate() { } } includes/class-cf7-customizer-activator.php000064400000001154147600046700015046 0ustar00 */ class Cf7_Customizer_Activator { /** * Short Description. (use period) * * Long Description. * * @since 1.0.0 */ public static function activate() { } } languages/cf7-styler-vi.po000064400000113720147600046700011475 0ustar00msgid "" msgstr "" "Language: vi\n" "POT-Creation-Date: 2020-02-06 11:35+0000\n" "Plural-Forms: nplurals=1; plural=0;\n" "PO-Revision-Date: 2023-04-15 16:10+0000\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: WOW Style Contact Form 7\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: Vietnamese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- Chế độ phong cách -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "Chọn -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14 Ngày Tòa án" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "% s Được sử dụng cho tất cả các biểu mẫu trên toàn cầu, hãy " "bấm \"sử dụng cho tất cả các biểu mẫu\" nếu bạn muốn sử dụng biểu mẫu hiện " "tại." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Tích hoạt phong cách cho tất cả các hình thức *" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Tích hoạt phong cách cho hình thức hiện tại" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "Là một lựa chọn thay thế, bạn có thể vô hiệu hóa việc làm sạch các phong " "cách như trên phrive architect:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Màu nền" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Hình nền" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Trước khi sử dụng biểu mẫu liên hệ kiểu Wow 7, bạn cần cài đặt và kích hoạt " "biểu mẫu liên hệ 7 Plugin." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "Bg Màu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "Bg Khả năng" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Bạch Ốc" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "biên giới" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Màu biên giới" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Biên giới radius" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Loại biên giới" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "biên giới rộng" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "nút biên giới" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Bút" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Tác giả: Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "hủy" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "Cf7 Customizer yêu cầu biểu mẫu liên hệ 7 Plugin để cài đặt và hoạt động. " "Bạn có thể tải xuống % S." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "Cf7 không được chọn" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "Cf7 phong cách" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Chế độ kiểm tra & Radiobuttons" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "phong cách sạch" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "rõ ràng" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "gần" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Màu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Hình thức liên hệ 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "Nội dung" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Sao chép các cài đặt phong cách hiện tại" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "Màn hình Cover" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "tạo ra" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Tạo nhiều phong cách" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Tạo một phong cách mới!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Phong cách hiện tại" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "Hiện tại % s Hình thức " "được thiết kế với % s " ". Như trong phiên bản miễn phí bạn có thể phong cách chỉ một biểu " "mẫu cùng một lúc và nếu bạn kích hoạt phong cách cho biểu mẫu hiện tại, " "phong cách sẽ được loại bỏ từ biểu mẫu khác." #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "Hiện tại % s Hình thức được thiết kế với % s " ". Như trong phiên bản miễn phí bạn có thể phong cách chỉ một biểu " "mẫu cùng một lúc và nếu bạn kích hoạt phong cách cho biểu mẫu hiện tại, " "phong cách sẽ được loại bỏ từ biểu mẫu khác." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "Tùy chỉnh CSS" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "Mã CSS tùy chỉnh" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Dashuất" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Hệ thống mặc định" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "xóa hình ảnh" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "xóa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Màn hình Desktop" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Loại phong cách vô hiệu cho tất cả các hình thức" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Chặn phong cách cho hình thức hiện tại" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "Bạn có muốn có được phiên bản Premium Styler miễn phí không? Sau đó Nhập Wp2Leads Pro " "License của bạn Hoặc có giấy phép Ở đây " "!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "Bạn muốn lưu các thay đổi trước khi rời khỏi trang?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "DOTTED" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Double Đôi" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Duplicate form trong cột thứ hai" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "Có sẵn cho tất cả các hình thức" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Lỗi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Màn hình Full Screen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Font gia đình" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "font kích thước" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Phong cách Font" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "font trọng lượng" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Hình ảnh BG & Màu sắc" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Hình thức Customize" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Hình thức không được chọn" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Hình thức padding, margin & border" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Form văn bản" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "màn hình đầy đủ" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "Trưởng" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Chiều dài horizontal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Màu sắc Hover BG" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "Hover Màu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Hover Text Màu sắc" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Làm thế nào để biến hình thức liên hệ của bạn Form7 thành một hình thức " "chuyển đổi và dễ sử dụng và hình thức liên hệ theo phong cách chuyên nghiệp, " "máy phát sáng \"học\" hoặc hình thức chụp mắt" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "Https: //Saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "Https: //Saleswonder.Biz/Blog/4Free-Contact-Form-7-Cf7-Formular-Und-Klick-" "Tipp-Einfach-Verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "Tôi hài lòng với điều này! tiết kiệm" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Nếu bạn đang sử dụng các nhà xây dựng trang, chẳng hạn như thrive architect, " "Optimizepress vv, vui lòng kiểm tra chúng tôi Trang cơ sở kiến thức Để giải " "quyết các vấn đề có thể" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Nếu bạn cần để tải lên phong cách kế hoạch bên trong Nhãn " "chỉ trên một số trang, bạn có thể làm điều đó trên frontend bằng cách sử " "dụng nút \"Cf7 Styler\". Tính năng này chỉ có sẵn cho các loại bài đăng duy " "nhất (các trang, bài đăng, sản phẩm, v.v.) Và trong phiên bản premium. Bạn " "không thể làm điều đó trên các trang lưu trữ (Blog, danh sách sản phẩm, v.v.)" " trong trường hợp này bạn cần sử dụng cài đặt toàn cầu." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Nếu bạn cần để tải lên phong cách kế hoạch bên trong Nhãn " "chỉ trên một số trang, bạn có thể làm điều đó trên frontend bằng cách sử " "dụng nút \"Cf7 Styler\". Tính năng này chỉ có sẵn cho các loại bài đăng duy " "nhất (các trang, bài đăng, sản phẩm, vv). Bạn không thể làm điều đó trên các " "trang lưu trữ (Blog, danh sách sản phẩm, v.v.) trong trường hợp này bạn cần " "sử dụng cài đặt toàn cầu." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "Cài đặt nền hình ảnh có sẵn trong chế độ sống chỉ cho phiên bản chuyên " "nghiệp. Bạn có thể kiểm tra nó trong chế độ xem trước \"trình hiện tại\", " "nhưng nó sẽ không được tiết kiệm." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Hình ảnh Opacity" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Hình ảnh Position" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "hình ảnh kích thước" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Di sản" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Các trường nhập" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Tiêu đề Input Style Scheme" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Ấn Độ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Ý" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Tags màu sắc" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Tag: font kích thước" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Label thiết lập" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Giấy phép" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "đường cao" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Màu trái" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Màu trái hover" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Cài đặt Links" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "Sống" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Load Styles trong Tag" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "Load Styles bên trong Tag trên trang này" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Tạo Checkbox item một theo dòng" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "Tải rộng đầy đủ?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "Làm cho các lĩnh vực nhập rộng đầy đủ?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Tạo các mục Radiobutton một cho mỗi dòng" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Margin" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Mobile xem" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "Không" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "Không có biểu mẫu liên hệ 7 mục để xem trước" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "Không có biểu mẫu được phong cách với biểu mẫu phong cách, nhấp vào nút " "\"Aktivate style for current form\" để áp dụng biểu mẫu hiện tại cho biểu " "mẫu này." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "Không có plugin được chọn để cài đặt" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "Không có phong cách được lựa chọn" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "bình thường" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Oblique" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "Một theo dòng phong cách cho Checkboxes và Radiobuttons trong chế độ sống " "chỉ cho phiên bản chuyên nghiệp. Bạn có thể kiểm tra nó trong chế độ xem " "trước \"trình hiện tại\", nhưng nó sẽ không được tiết kiệm." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Opacity" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Mở Styler" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "Opt-in để xem tài khoản" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "Kích thước gốc" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Outline Khác" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "ngoài ra" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Padding" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Vui lòng nhập kiểu scheme title" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Dự báo" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "chế độ Preview" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Preview không thể" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Radius" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "Lặp lại cả hai" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "Lặp lại horizontal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "Lặp lại vertical" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Khởi động trở lại default" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Ridge" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "tiết kiệm" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "cứu rỗi" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Nhìn Column 2" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Chọn biểu mẫu kiểu cho biểu mẫu này" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Cài đặt" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "Cài đặt được tiết kiệm như" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "bóng tối" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "Màu bóng" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Vị trí bóng" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Sức mạnh" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Một số chủ đề hoặc xây dựng trang (Fe. Thrive kiến trúc sư, Optimizepress vv)" " Có thể loại bỏ các phong cách Inline bên trong Tag . " "Loading style scheme trong Tag sẽ khắc phục vấn đề này và " "hiển thị các biểu mẫu của bạn được phong cách." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "Mô hình Split" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "View phân chia" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Tải ra radius" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Bắt đầu styling" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Bắt đầu với việc tạo một kế hoạch phong cách mặc định" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Bắt đầu bằng cách tạo ra bạn Hình thức " "liên lạc đầu tiên 7 " #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "Bắt đầu thử nghiệm miễn phí 14 ngày của bạn với tất cả các chức năng chuyên " "nghiệp Ở đây !" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Bước 1 Nhấp vào liên kết \"ed with thrive architect\" trong danh sách trang " "Admin" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "Bước 2 Nhấp vào biểu tượng \"Settings\" trên menu thẳng" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "Bước 3 “Cài đặt nâng cao” => “CSS trong Phần »" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "Bước 4 Hãy chắc chắn rằng \"không xóa CSS từ » Đã kiểm tra" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Bước 5 Nhấp vào nút \"save work\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "Phong cách tất cả các hình thức trong một cú nhấp chuột toàn cầu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "Phong cách mỗi hình thức với bất kỳ phong cách kế hoạch riêng lẻ" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Thiết kế phong cách bị chặn cho tất cả các hình thức" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Chế độ phong cách bị chặn cho hình thức này" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Chế độ phong cách được kích hoạt cho hình thức này" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "Chế độ phong cách không được chọn" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "Danh sách Style Schemes" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Phong cách Preview" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "Thiết lập Style Schemes" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "Phong cách sẽ được tải lên trong Tag" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "Phong cách sẽ được tải lên trong Tag" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Thành công" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "Hỗ trợ & KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Hình ảnh Tablet" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Text màu sắc" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Hình thức này được thiết kế với % s Trên toàn cầu, bạn có " "thể phong cách nó với phong cách hiện tại." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Hình thức này được thiết kế với % s , bạn có thể phong cách " "nó với phong cách hiện tại." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Hình thức này được phong cách với biểu mẫu phong cách hiện tại, bạn có thể " "tắt nó và sử dụng cài đặt toàn cầu." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Hình thức này không được phong cách với bất kỳ biểu mẫu phong cách nào, bạn " "có thể bật biểu mẫu hiện tại cho biểu mẫu này hoặc thiết lập biểu mẫu phong " "cách toàn cầu bằng cách nhấp chuột \"làm cho tất cả các biểu mẫu\" bên dưới " "tiêu đề biểu mẫu." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Hệ thống này không tồn tại" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Phong cách này cho phép toàn cầu cho tất cả các hình thức. Nếu bạn muốn sử " "dụng phong cách chủ đề của bạn cho biểu mẫu liên hệ 7 nhấp vào nút \"không " "thể\"." #. Author of the plugin msgid "Tobias Conrad" msgstr "Đạo diễn Tobias Conrad" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "hướng dẫn" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "không phong cách" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "nâng cấp lên pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Tải hình ảnh" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "chiều dài vertical" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Chào mừng" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "rộng" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "Hình thức liên hệ WOW 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "Có" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "Bạn có thể tạo một biểu mẫu phong cách mới với các cài đặt biểu mẫu hiện tại." " Nếu bạn muốn tạo phong cách trắng Schem Unchek Checkbox bên dưới." #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "Bạn không thể xóa" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "Bạn không thể sử dụng cài đặt này trên kiểu trang này" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "Bạn không có bất kỳ hình thức liên lạc 7 mục" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "Bạn không sử dụng bất kỳ biểu mẫu kiểu nào cho biểu mẫu liên hệ 7. Nhấp vào " "\"sử dụng cho tất cả các biểu mẫu\" để sử dụng biểu mẫu hiện tại trên toàn " "cầu." languages/cf7-styler-vi.mo000064400000051370147600046700011474 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'()))*;*6+I+++ + ++ , ,,,,,,,- ----%-- ...#. =.3H.|......R011 12!222>E202F2C3@4 G4$T4y44444444%5:5#J5n5 555 55516%7#48X:[<o<<<=== ====> > #> 1><>M>_>(f>>>$>>5?2D?w? ~??=??3@,@@A A B B B>B SB aBkB$sB BBBBBBB"C6C %s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Language: vi POT-Creation-Date: 2020-02-06 11:35+0000 Plural-Forms: nplurals=1; plural=0; PO-Revision-Date: 2023-04-15 16:10+0000 X-Generator: Loco https://localise.biz/ Project-Id-Version: WOW Style Contact Form 7 Report-Msgid-Bugs-To: Last-Translator: Tobias support@saleswonder.biz Language-Team: Vietnamese MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- Chế độ phong cách -Chọn -14 Ngày Tòa án% s Được sử dụng cho tất cả các biểu mẫu trên toàn cầu, hãy bấm "sử dụng cho tất cả các biểu mẫu" nếu bạn muốn sử dụng biểu mẫu hiện tại.Tích hoạt phong cách cho tất cả các hình thức *Tích hoạt phong cách cho hình thức hiện tạiLà một lựa chọn thay thế, bạn có thể vô hiệu hóa việc làm sạch các phong cách như trên phrive architect:Bg MàuBg Khả năngMàu nềnHình nềnTrước khi sử dụng biểu mẫu liên hệ kiểu Wow 7, bạn cần cài đặt và kích hoạt biểu mẫu liên hệ 7 Plugin.Bạch Ốcbiên giớiMàu biên giớiBiên giới radiusLoại biên giớibiên giới rộngnút biên giớiBútTác giả: Takayuki MiyoshiCf7 Customizer yêu cầu biểu mẫu liên hệ 7 Plugin để cài đặt và hoạt động. Bạn có thể tải xuống % S.Cf7 không được chọnCf7 phong cáchhủyChế độ kiểm tra & Radiobuttonsphong cách sạchrõ rànggầnMàuHình thức liên hệ 7Nội dungSao chép các cài đặt phong cách hiện tạiMàn hình Covertạo raTạo nhiều phong cáchTạo một phong cách mới!Phong cách hiện tạiHiện tại % s Hình thức được thiết kế với % s . Như trong phiên bản miễn phí bạn có thể phong cách chỉ một biểu mẫu cùng một lúc và nếu bạn kích hoạt phong cách cho biểu mẫu hiện tại, phong cách sẽ được loại bỏ từ biểu mẫu khác.Hiện tại % s Hình thức được thiết kế với % s . Như trong phiên bản miễn phí bạn có thể phong cách chỉ một biểu mẫu cùng một lúc và nếu bạn kích hoạt phong cách cho biểu mẫu hiện tại, phong cách sẽ được loại bỏ từ biểu mẫu khác.Tùy chỉnh CSSMã CSS tùy chỉnhDashuấtHệ thống mặc địnhxóa hình ảnhMàn hình DesktopLoại phong cách vô hiệu cho tất cả các hình thứcChặn phong cách cho hình thức hiện tạiBạn có muốn có được phiên bản Premium Styler miễn phí không? Sau đó Nhập Wp2Leads Pro License của bạn Hoặc có giấy phép Ở đây !Bạn muốn lưu các thay đổi trước khi rời khỏi trang?DOTTEDDouble ĐôiDuplicate form trong cột thứ haiLỗiMàn hình Full ScreenFont gia đìnhfont kích thướcPhong cách Fontfont trọng lượngHình ảnh BG & Màu sắcHình thức CustomizeHình thức padding, margin & borderForm văn bảnHình thức không được chọnmàn hình đầy đủTrưởngChiều dài horizontalMàu sắc Hover BGHover MàuHover Text Màu sắcLàm thế nào để biến hình thức liên hệ của bạn Form7 thành một hình thức chuyển đổi và dễ sử dụng và hình thức liên hệ theo phong cách chuyên nghiệp, máy phát sáng "học" hoặc hình thức chụp mắtTôi hài lòng với điều này! tiết kiệmNếu bạn đang sử dụng các nhà xây dựng trang, chẳng hạn như thrive architect, Optimizepress vv, vui lòng kiểm tra chúng tôi Trang cơ sở kiến thức Để giải quyết các vấn đề có thểNếu bạn cần để tải lên phong cách kế hoạch bên trong Nhãn chỉ trên một số trang, bạn có thể làm điều đó trên frontend bằng cách sử dụng nút "Cf7 Styler". Tính năng này chỉ có sẵn cho các loại bài đăng duy nhất (các trang, bài đăng, sản phẩm, v.v.) Và trong phiên bản premium. Bạn không thể làm điều đó trên các trang lưu trữ (Blog, danh sách sản phẩm, v.v.) trong trường hợp này bạn cần sử dụng cài đặt toàn cầu.Nếu bạn cần để tải lên phong cách kế hoạch bên trong Nhãn chỉ trên một số trang, bạn có thể làm điều đó trên frontend bằng cách sử dụng nút "Cf7 Styler". Tính năng này chỉ có sẵn cho các loại bài đăng duy nhất (các trang, bài đăng, sản phẩm, vv). Bạn không thể làm điều đó trên các trang lưu trữ (Blog, danh sách sản phẩm, v.v.) trong trường hợp này bạn cần sử dụng cài đặt toàn cầu.Hình ảnh OpacityHình ảnh Positionhình ảnh kích thướcCài đặt nền hình ảnh có sẵn trong chế độ sống chỉ cho phiên bản chuyên nghiệp. Bạn có thể kiểm tra nó trong chế độ xem trước "trình hiện tại", nhưng nó sẽ không được tiết kiệm.Di sảnCác trường nhậpTiêu đề Input Style SchemeẤn ĐộÝTags màu sắcTag: font kích thướcLabel thiết lậpGiấy phépđường caoMàu tráiMàu trái hoverCài đặt LinksSốngLoad Styles trong TagLoad Styles bên trong Tag trên trang nàyTạo Checkbox item một theo dòngTải rộng đầy đủ?Làm cho các lĩnh vực nhập rộng đầy đủ?Tạo các mục Radiobutton một cho mỗi dòngMarginMobile xemKhôngKhông có biểu mẫu liên hệ 7 mục để xem trướcKhông có biểu mẫu được phong cách với biểu mẫu phong cách, nhấp vào nút "Aktivate style for current form" để áp dụng biểu mẫu hiện tại cho biểu mẫu này.Không có plugin được chọn để cài đặtKhông có phong cách được lựa chọnbình thườngObliqueMột theo dòng phong cách cho Checkboxes và Radiobuttons trong chế độ sống chỉ cho phiên bản chuyên nghiệp. Bạn có thể kiểm tra nó trong chế độ xem trước "trình hiện tại", nhưng nó sẽ không được tiết kiệm.OpacityMở StylerOpt-in để xem tài khoảnKích thước gốcOutline Khácngoài raPaddingVui lòng nhập kiểu scheme titleDự báoPreview không thểchế độ PreviewRadiusLặp lại cả haiLặp lại horizontalLặp lại verticalKhởi động trở lại defaultRidgetiết kiệmcứu rỗiNhìn Column 2Chọn biểu mẫu kiểu cho biểu mẫu nàyCài đặtCài đặt được tiết kiệm nhưbóng tốiMàu bóngVị trí bóngSức mạnhMột số chủ đề hoặc xây dựng trang (Fe. Thrive kiến trúc sư, Optimizepress vv) Có thể loại bỏ các phong cách Inline bên trong Tag . Loading style scheme trong Tag sẽ khắc phục vấn đề này và hiển thị các biểu mẫu của bạn được phong cách.Mô hình SplitView phân chiaTải ra radiusBắt đầu stylingBắt đầu với việc tạo một kế hoạch phong cách mặc địnhBắt đầu bằng cách tạo ra bạn Hình thức liên lạc đầu tiên 7 Bắt đầu thử nghiệm miễn phí 14 ngày của bạn với tất cả các chức năng chuyên nghiệp Ở đây !Bước 1 Nhấp vào liên kết "ed with thrive architect" trong danh sách trang AdminBước 2 Nhấp vào biểu tượng "Settings" trên menu thẳngBước 3 “Cài đặt nâng cao” => “CSS trong Phần »Bước 4 Hãy chắc chắn rằng "không xóa CSS từ » Đã kiểm traBước 5 Nhấp vào nút "save work"Phong cách tất cả các hình thức trong một cú nhấp chuột toàn cầuPhong cách mỗi hình thức với bất kỳ phong cách kế hoạch riêng lẻThiết kế phong cách bị chặn cho tất cả các hình thứcChế độ phong cách bị chặn cho hình thức nàyChế độ phong cách được kích hoạt cho hình thức nàyChế độ phong cách không được chọnDanh sách Style SchemesPhong cách PreviewThiết lập Style SchemesPhong cách sẽ được tải lên trong TagPhong cách sẽ được tải lên trong TagThành côngHỗ trợ & KBHình ảnh TabletText màu sắcPhong cách này cho phép toàn cầu cho tất cả các hình thức. Nếu bạn muốn sử dụng phong cách chủ đề của bạn cho biểu mẫu liên hệ 7 nhấp vào nút "không thể".Hình thức này được thiết kế với % s Trên toàn cầu, bạn có thể phong cách nó với phong cách hiện tại.Hình thức này được thiết kế với % s , bạn có thể phong cách nó với phong cách hiện tại.Hình thức này được phong cách với biểu mẫu phong cách hiện tại, bạn có thể tắt nó và sử dụng cài đặt toàn cầu.Hình thức này không được phong cách với bất kỳ biểu mẫu phong cách nào, bạn có thể bật biểu mẫu hiện tại cho biểu mẫu này hoặc thiết lập biểu mẫu phong cách toàn cầu bằng cách nhấp chuột "làm cho tất cả các biểu mẫu" bên dưới tiêu đề biểu mẫu.Hệ thống này không tồn tạiĐạo diễn Tobias Conradhướng dẫnkhông phong cáchnâng cấp lên proTải hình ảnhchiều dài verticalHình thức liên hệ WOW 7Chào mừngrộngCóBạn có thể tạo một biểu mẫu phong cách mới với các cài đặt biểu mẫu hiện tại. Nếu bạn muốn tạo phong cách trắng Schem Unchek Checkbox bên dưới.Bạn không thể xóaBạn không thể sử dụng cài đặt này trên kiểu trang nàyBạn không có bất kỳ hình thức liên lạc 7 mụcBạn không sử dụng bất kỳ biểu mẫu kiểu nào cho biểu mẫu liên hệ 7. Nhấp vào "sử dụng cho tất cả các biểu mẫu" để sử dụng biểu mẫu hiện tại trên toàn cầu.xóaCó sẵn cho tất cả các hình thứcHttps: //Saleswonder.bizHttps: //Saleswonder.Biz/Blog/4Free-Contact-Form-7-Cf7-Formular-Und-Klick-Tipp-Einfach-Verbinden/languages/cf7-styler-ur.po000064400000121151147600046700011502 0ustar00msgid "" msgstr "" "Language: ur\n" "POT-Creation-Date: 2023-04-15 15:26+0000\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "PO-Revision-Date: 2023-04-15 16:01+0000\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: WOW Style Contact Form 7\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: Urdu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- غیر فعال سٹائل ڈیزائن -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "منتخب کریں -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14 دن کی سماعت" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s عالمی سطح پر تمام فارم کے لئے استعمال کیا جاتا ہے، اگر " "آپ موجودہ نظام کا استعمال کرنا چاہتے ہیں تو \"تمام فارم کے لئے استعمال " "کریں\" پر کلک کریں." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "تمام شکلوں کے لئے سٹائل فعال کریں *" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "موجودہ شکل کے لئے سٹائل فعال کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "ایک متبادل کے طور پر آپ کو اس طرح کے سٹائل کو صاف کرنے کی اجازت نہیں دے سکتے " "ہیں جیسا کہ فریو آرکیٹیکٹ:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "پس منظر کا رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "تصویر کے پس منظر" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Wow سٹائل رابطہ فارم 7 کا استعمال کرنے سے پہلے، آپ کو انسٹال اور فعال کرنے " "کی ضرورت ہے رابطہ فارم 7 پلگ ان." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "بی سی رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "بی جے پی کے متنازعہ" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "بلیو ریڈیو" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "سرحدیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "سرحد کا رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "سرحد راجستہ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "سرحدی اقسام" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "سرحد کی وسیع" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "بٹن کی سرحد" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "بٹنیں" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "کی طرف سے Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "منسوخ" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "Cf7 Customizer رابطہ فارم 7 پلگ ان کو انسٹال اور فعال کرنے کی ضرورت ہے. آپ " "کو % S ڈاؤن لوڈ کر سکتے ہیں." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "Cf7 فارم منتخب نہیں کیا گیا ہے" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "Cf7 سٹائلر" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "چیک باکس اور ریڈیو بٹن" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "صاف سٹائل" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "واضح ہے" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "قریب ہے" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "رنگیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "فہرست نمبر 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "شامل کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "موجودہ سٹائل سکرپٹ کی ترتیبات کاپی کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "کوریج" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "تخلیق" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "کثیر سٹائل سکرپٹ بنائیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "ایک نئے سٹائل ڈیزائن بنائیں!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "موجودہ سٹائل" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "فی الحال % کے s شکل کے " "ساتھ اسٹائل کیا جاتا ہے % " "کے s → جیسا کہ مفت ورژن میں آپ صرف ایک فارم کو ایک بار میں سٹائل کر " "سکتے ہیں اور اگر آپ موجودہ فارم کے لئے سٹائل کو فعال کرتے ہیں تو سٹائل کو " "دوسرے فارم سے ہٹا دیا جائے گا." #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "فی الحال % کے s شکل کے ساتھ اسٹائل کیا جاتا ہے % کے " "s → جیسا کہ مفت ورژن میں آپ صرف ایک فارم کو ایک بار میں سٹائل کر " "سکتے ہیں اور اگر آپ موجودہ فارم کے لئے سٹائل کو فعال کرتے ہیں تو سٹائل کو " "دوسرے فارم سے ہٹا دیا جائے گا." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "CSS کے مترادفات" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "CSS کوڈ کا استعمال کریں" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "ڈھونڈنا" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "ڈفالٹ سسٹم" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "تصویر کو ہٹا دیں" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "حذف کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "ڈیسک ٹاپ دیکھیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "تمام شکلوں کے لئے غیر فعال سٹائل" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "موجودہ شکل کے لئے غیر فعال سٹائل" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "کیا آپ مفت کے لئے Styler پریمیوم ورژن حاصل کرنا چاہتے ہیں؟ اور پھر آپ کے Wp2Leads پرو " "لائسنس میں داخل کریں یا لائسنس حاصل کریں یہاں یہاں " "ہے !" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "کیا آپ صفحات کو چھوڑنے سے پہلے تبدیلیوں کو محفوظ کرنا چاہتے ہیں؟" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "ڈوٹنگ" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "دوہرا" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "دوسری ستون میں دوگنا فارم" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "تمام اقسام کے لئے دستیاب" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "غلطی" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "مکمل اسکرین سے باہر نکلنا" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "فینٹ خاندان" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "فونٹ سائز" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "فونٹ سٹائل" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "وزن کا چمچ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "شکل BG تصویر اور رنگ" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "شکل کی تصدیق" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "فورم منتخب نہیں کیا گیا" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "فارم پڈنگ، مارجن اور سرحد" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "فارم متن" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "پورے اسکرین" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "زراعت" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "افقی لمبائی" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Hover BG رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "ہاؤس رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Hover متن کا رنگ" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "آپ کے رابطے فارم 7 کو تبدیل کرنے کے لئے کس طرح ایک تبدیل کرنے اور استعمال " "کرنے کے لئے آسان اور پرو سٹائل رابطے فارم، \"سائن اپ\" پاؤڈر جنریٹر یا ایک " "آنکھ پکڑنے فارم" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "↑ انگریزی ویکیپیڈیا کے مشارکین۔ //Saleswonder.Biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "Https: //Saleswonder.Biz/Blog/4Free-Contact-Form-7-Cf7-Formular-Und-Klick-" "Tipp-Einfach-Verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "میں اس سے خوش ہوں! بچاؤ" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "اگر آپ صفحات کی تعمیر کرنے والے استعمال کر رہے ہیں، جیسے فریم آرکیٹیکٹ، " "Optimizepress وغیرہ، براہ مہربانی ہماری چیک کریں علم کی بنیاد پر صفحہ " "ممکنہ مسائل کو حل کرنے کے لئے" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "اگر آپ کے اندر اندر سٹائل ڈیزائن لوڈ کرنے کی ضرورت ہے صرف " "کچھ صفحات پر ٹیگ کریں، آپ \"Cf7 Styler\" بٹن کا استعمال کرتے ہوئے frontend " "پر کر سکتے ہیں. یہ خصوصیت صرف ایک پوسٹ قسم کے لئے دستیاب ہے (صفحات، پوسٹس، " "مصنوعات وغیرہ) اس کے علاوہ، پرمیوم ورژن میں. آپ اس آرکائیو صفحات پر نہیں کر " "سکتے ہیں (بلاگ، مصنوعات کی فہرست وغیرہ) اس صورت میں آپ کو عالمی ترتیبات کا " "استعمال کرنے کی ضرورت ہے." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "اگر آپ کے اندر اندر سٹائل ڈیزائن لوڈ کرنے کی ضرورت ہے صرف " "کچھ صفحات پر ٹیگ کریں، آپ \"Cf7 Styler\" بٹن کا استعمال کرتے ہوئے frontend " "پر کر سکتے ہیں. یہ خصوصیت صرف ایک پوسٹ قسم (صفحات، پوسٹس، مصنوعات وغیرہ) کے " "لئے دستیاب ہے. آپ اس آرکائیو صفحات پر نہیں کر سکتے ہیں (بلاگ، مصنوعات کی " "فہرست وغیرہ) اس صورت میں آپ کو عالمی ترتیبات کا استعمال کرنے کی ضرورت ہے." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "تصویر کے پس منظر کی ترتیبات صرف پیشہ ورانہ ورژن کے لئے لائیو موڈ میں دستیاب " "ہیں. آپ اسے \"ابھی موجودہ سٹائل\" موڈ میں ٹیسٹ کر سکتے ہیں، لیکن یہ محفوظ " "نہیں کیا جائے گا." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "تصویر کے مترادفات Opacity" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "تصویر کی پوزیشن" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "تصویر کی سائز" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "میراث" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "داخلہ کے میدان" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "انٹرویو سٹائل سکرپٹ" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "انسٹا" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "ایتالیائی" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "ٹیگ کے رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "ٹیگ کیا گیا ہے Font Size" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Labels ترتیبات" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "لائسنس" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "لائن کی اونچائی" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "بائیں رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "ہاؤس ہاؤس رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "چپ رہنے کا نظام" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "لائیو" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "پرنٹ سٹائل میں Tag کے" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "انٹرویو سٹائل داخلہ Tag اس صفحے پر" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "چیک باکس اشیاء کو ایک لائن پر بنائیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "مکمل طور پر پھیلاؤ ہے؟" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "انٹرویو فیلڈ مکمل وسیع بنائیں؟" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Radiobutton اشیاء کو ایک لائن پر بنائیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "مارکیٹ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "موبائل دیکھیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "نہیں نہیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "کوئی رابطہ فارم 7 اشیاء کے لئے پیش نظارہ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "کوئی فارم اسٹائل ڈیزائن کے ساتھ سٹائل نہیں ہے، اس فارم کے لئے موجودہ ڈیزائن " "کو لاگو کرنے کے لئے \"آج کے فارم کے لئے سٹائل فعال کریں\" بٹن پر کلک کریں." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "کوئی پلگ ان منتخب نہیں کیا گیا انسٹال کرنے کے لئے" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "کوئی نمونہ منتخب نہیں کیا گیا" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "معمولی" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "بلیک" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "چیک باکس اور ریڈیو بٹونز کے لئے ایک لائن سٹائل لائیو موڈ میں صرف پیشہ ور " "ورانہ ورژن کے لئے. آپ اسے \"ابھی موجودہ سٹائل\" موڈ میں ٹیسٹ کر سکتے ہیں، " "لیکن یہ محفوظ نہیں کیا جائے گا." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "ناخوشگوار" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "کھولیں سٹائل" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "اکاؤنٹ دیکھنے کے لئے opt-in" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "اصل سائز" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "آؤٹ لائن" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "باہر نکلنا" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "پڈنگ" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "براہ مہربانی داخل کریں سٹائل سکرپٹ عنوان" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "پیشگوئی" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "پیشگوئی کا طریقہ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "غیر معمولی پیش نظر" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "ریڈیو" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "دو بار تکرار کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "افقی طور پر تکرار کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "ورٹیکاٹ کی تکرار" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "دوبارہ ڈاؤن لوڈ کریں default" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "ریڈنگ" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "بچاؤ" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "بچاؤ" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "دوسری سٹائل کا منظر" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "اس فارم کے لئے سٹائل سکرپٹ کا انتخاب کریں" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "ترتیبات" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "ترتیبات کو محفوظ کریں جیسے" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "سایہ ہے" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "سایہ کا رنگ" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "سایہ کی پوزیشن" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "مضبوطی" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "کچھ موضوعات یا صفحہ بنانے والے (Fe. ٹریو آرکیٹیکٹ، Optimizepress وغیرہ " "انٹرویو سٹائل کو داخل کر سکتے ہیں ٹیگ کریں لوڈنگ سٹائل کے " "نظام کے اندر اندر ٹیگ اس مسئلے کو درست کرے گا اور آپ کے " "فارم سٹائل دکھائیں گے." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "تقسیم کا طریقہ" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "تقسیم نظر" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "راجکماری" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "شروع کرنے کے لئے سٹائل" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "ڈیفالٹ سٹائل سکرپٹ کی تخلیق سے شروع کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "آپ کی تخلیق کے ساتھ شروع سب سے پہلے " "رابطہ نمبر 7 " #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "تمام پیشہ ورانہ خصوصیات کے ساتھ آپ کے 14 دن مفت ٹیسٹ شروع کریں یہاں یہاں ہے !" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "مرحلہ 1 کلک کریں \"Edit with thrive architect\" لنک میں ایڈمن صفحہ فہرست" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "مرحلہ 2 \"Settings\" آیکون پر دائیں ورٹک مینو پر کلک کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "مرحلہ 3 \"مزید ترتیبات\" => \"CSS میں سیکشن »" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "مرحلہ 4 اس بات کو یقینی بنائیں کہ \"CSS سے نکالنے کے لئے نہیں چیک کیا " "گیا ہے" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "مرحلہ 5 \"Save Work\" کے بٹن پر کلک کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "سٹائل تمام فارم ایک کلک میں عالمی سطح پر" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "سٹائل ہر شکل کے ساتھ کسی بھی سٹائل ڈیزائن انفرادی طور پر" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "سٹائل سکرپٹ تمام شکلوں کے لئے غیر فعال ہے" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "اس شکل کے لئے سٹائل سکرپٹ غیر فعال ہے" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "اس فارم کے لئے سٹائل سکرپٹ کی اجازت" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "نمونے کا انتخاب نہیں کیا گیا ہے" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "سٹائل کی فہرست" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "سٹائل سکرپٹ پیش نظر" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "سٹائل سٹائل سٹائل" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "سٹائل میں شامل کیا جائے گا Tag کے" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "سٹائل میں شامل کیا جائے گا Tag کے" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "کامیابی کے" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "کی حمایت & KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "ٹیبلٹ ویڈیوز" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "متن کا رنگ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "اس طرح کے طور پر اس طرح کے طور پر % کے s عالمی سطح پر، آپ " "اسے موجودہ سٹائل کے ساتھ سٹائل کر سکتے ہیں." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "اس طرح کے طور پر اس طرح کے طور پر % کے s , آپ اسے موجودہ " "سٹائل کے ساتھ سٹائل کر سکتے ہیں." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "یہ فارم موجودہ سٹائل کے ساتھ سٹائل کیا جاتا ہے، آپ اسے غیر فعال کر سکتے ہیں " "اور عالمی ترتیبات کا استعمال کرسکتے ہیں." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "یہ فارم کسی بھی سٹائل ڈیزائن کے ساتھ سٹائل نہیں ہے، آپ اس فارم کے لئے موجودہ " "ڈیزائن کو فعال کر سکتے ہیں یا \"تمام فارم کے لئے استعمال کریں\" پر کلک کرکے " "عالمی سٹائل ڈیزائن کو ترتیب دے سکتے ہیں." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "یہ نظام موجود نہیں ہے" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "اس سٹائل کے نظام کو عالمی سطح پر تمام شکلوں کے لئے ممکن کیا گیا ہے. اگر آپ " "رابطہ فارم کے لئے آپ کے موضوع کی سٹائل کا استعمال کرنا چاہتے ہیں تو 7 کلک " "کریں \"غائب\" بٹن." #. Author of the plugin msgid "Tobias Conrad" msgstr "ٹوبیاس کانڈ" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "ٹویٹرنگ" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "غیر معمولی" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "پرو کے لئے اپ گریڈ کریں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "تصویر اپ لوڈ کریں" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "ورٹیکا طویل" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "خوش آمدید" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "وسیع" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "WOW سٹائل رابطہ فارم 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "جی ہاں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "آپ موجودہ سٹائل کی ترتیبات کے ساتھ نئے سٹائل ڈیزائن بن سکتے ہیں. اگر آپ " "مندرجہ ذیل میں سفید سٹائل Schem Unchek Checkbox بنانا چاہتے ہیں." #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "آپ کو حذف نہیں کر سکتے" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "آپ اس صفحے کی قسم پر اس ترتیب کا استعمال نہیں کرسکتے ہیں" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "آپ کے پاس کوئی رابطہ فارم نہیں ہے 7 اشیاء" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "آپ کو رابطہ فارم 7 کے لئے کسی بھی سٹائل ڈیزائن کا استعمال نہیں کرتے ہیں. کلک " "کریں \"تمام فارم کے لئے استعمال کریں\" موجودہ نظام کو عالمی سطح پر استعمال " "کرنے کے لئے." languages/cf7-styler-ur.mo000064400000056632147600046700011512 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'(+))*&*>9+<x++l,"},,,,- ----- . .!+.M.3.'/ 8/(C/l/ ~/ / ///H/ 0 !0+,03X000224'L4t4444:4: 5H5t6 J7 U7.`77.77778"898.P88*88 88899*9(J:os:;t>(A*AGA$`A BB$B BBB#C%C ;CHCeCwCC C6CNCA=D(D8D=D E,EFEGXEEXF5G 6GCG7LGHH,HHHHIJIdI!sII II(IJ-'J UJ`JiJ#rJJJJ0J !K/KDK _KlKLM$M(5MI^M|M@%NffOYOI'PqP9PH2Qe{QJQB,R?oR8RR#S 'S>HS>SSSST T;UUVSuW&XXYY))YSYsY$YYY YY'ZdZHE[[\,\M\a8]- disable style scheme -- select -14-days Trial%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Language: ur POT-Creation-Date: 2023-04-15 15:26+0000 Plural-Forms: nplurals=2; plural=n != 1; PO-Revision-Date: 2023-04-15 16:01+0000 X-Generator: Loco https://localise.biz/ Project-Id-Version: WOW Style Contact Form 7 Report-Msgid-Bugs-To: Last-Translator: Tobias support@saleswonder.biz Language-Team: Urdu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- غیر فعال سٹائل ڈیزائن -منتخب کریں -14 دن کی سماعت%s عالمی سطح پر تمام فارم کے لئے استعمال کیا جاتا ہے، اگر آپ موجودہ نظام کا استعمال کرنا چاہتے ہیں تو "تمام فارم کے لئے استعمال کریں" پر کلک کریں.تمام شکلوں کے لئے سٹائل فعال کریں *موجودہ شکل کے لئے سٹائل فعال کریںایک متبادل کے طور پر آپ کو اس طرح کے سٹائل کو صاف کرنے کی اجازت نہیں دے سکتے ہیں جیسا کہ فریو آرکیٹیکٹ:بی سی رنگبی جے پی کے متنازعہپس منظر کا رنگتصویر کے پس منظرWow سٹائل رابطہ فارم 7 کا استعمال کرنے سے پہلے، آپ کو انسٹال اور فعال کرنے کی ضرورت ہے رابطہ فارم 7 پلگ ان.بلیو ریڈیوسرحدیںسرحد کا رنگسرحد راجستہسرحدی اقسامسرحد کی وسیعبٹن کی سرحدبٹنیںکی طرف سے Takayuki MiyoshiCf7 Customizer رابطہ فارم 7 پلگ ان کو انسٹال اور فعال کرنے کی ضرورت ہے. آپ کو % S ڈاؤن لوڈ کر سکتے ہیں.Cf7 فارم منتخب نہیں کیا گیا ہےCf7 سٹائلرمنسوخچیک باکس اور ریڈیو بٹنصاف سٹائلواضح ہےقریب ہےرنگیںفہرست نمبر 7شامل کریںموجودہ سٹائل سکرپٹ کی ترتیبات کاپی کریںکوریجتخلیقکثیر سٹائل سکرپٹ بنائیںایک نئے سٹائل ڈیزائن بنائیں!موجودہ سٹائلفی الحال % کے s شکل کے ساتھ اسٹائل کیا جاتا ہے % کے s → جیسا کہ مفت ورژن میں آپ صرف ایک فارم کو ایک بار میں سٹائل کر سکتے ہیں اور اگر آپ موجودہ فارم کے لئے سٹائل کو فعال کرتے ہیں تو سٹائل کو دوسرے فارم سے ہٹا دیا جائے گا.فی الحال % کے s شکل کے ساتھ اسٹائل کیا جاتا ہے % کے s → جیسا کہ مفت ورژن میں آپ صرف ایک فارم کو ایک بار میں سٹائل کر سکتے ہیں اور اگر آپ موجودہ فارم کے لئے سٹائل کو فعال کرتے ہیں تو سٹائل کو دوسرے فارم سے ہٹا دیا جائے گا.CSS کے مترادفاتCSS کوڈ کا استعمال کریںڈھونڈناڈفالٹ سسٹمتصویر کو ہٹا دیںڈیسک ٹاپ دیکھیںتمام شکلوں کے لئے غیر فعال سٹائلموجودہ شکل کے لئے غیر فعال سٹائلکیا آپ مفت کے لئے Styler پریمیوم ورژن حاصل کرنا چاہتے ہیں؟ اور پھر آپ کے Wp2Leads پرو لائسنس میں داخل کریں یا لائسنس حاصل کریں یہاں یہاں ہے !کیا آپ صفحات کو چھوڑنے سے پہلے تبدیلیوں کو محفوظ کرنا چاہتے ہیں؟ڈوٹنگدوہرادوسری ستون میں دوگنا فارمغلطیمکمل اسکرین سے باہر نکلنافینٹ خاندانفونٹ سائزفونٹ سٹائلوزن کا چمچشکل BG تصویر اور رنگشکل کی تصدیقفارم پڈنگ، مارجن اور سرحدفارم متنفورم منتخب نہیں کیا گیاپورے اسکرینزراعتافقی لمبائیHover BG رنگہاؤس رنگHover متن کا رنگآپ کے رابطے فارم 7 کو تبدیل کرنے کے لئے کس طرح ایک تبدیل کرنے اور استعمال کرنے کے لئے آسان اور پرو سٹائل رابطے فارم، "سائن اپ" پاؤڈر جنریٹر یا ایک آنکھ پکڑنے فارممیں اس سے خوش ہوں! بچاؤاگر آپ صفحات کی تعمیر کرنے والے استعمال کر رہے ہیں، جیسے فریم آرکیٹیکٹ، Optimizepress وغیرہ، براہ مہربانی ہماری چیک کریں علم کی بنیاد پر صفحہ ممکنہ مسائل کو حل کرنے کے لئےاگر آپ کے اندر اندر سٹائل ڈیزائن لوڈ کرنے کی ضرورت ہے صرف کچھ صفحات پر ٹیگ کریں، آپ "Cf7 Styler" بٹن کا استعمال کرتے ہوئے frontend پر کر سکتے ہیں. یہ خصوصیت صرف ایک پوسٹ قسم کے لئے دستیاب ہے (صفحات، پوسٹس، مصنوعات وغیرہ) اس کے علاوہ، پرمیوم ورژن میں. آپ اس آرکائیو صفحات پر نہیں کر سکتے ہیں (بلاگ، مصنوعات کی فہرست وغیرہ) اس صورت میں آپ کو عالمی ترتیبات کا استعمال کرنے کی ضرورت ہے.اگر آپ کے اندر اندر سٹائل ڈیزائن لوڈ کرنے کی ضرورت ہے صرف کچھ صفحات پر ٹیگ کریں، آپ "Cf7 Styler" بٹن کا استعمال کرتے ہوئے frontend پر کر سکتے ہیں. یہ خصوصیت صرف ایک پوسٹ قسم (صفحات، پوسٹس، مصنوعات وغیرہ) کے لئے دستیاب ہے. آپ اس آرکائیو صفحات پر نہیں کر سکتے ہیں (بلاگ، مصنوعات کی فہرست وغیرہ) اس صورت میں آپ کو عالمی ترتیبات کا استعمال کرنے کی ضرورت ہے.تصویر کے مترادفات Opacityتصویر کی پوزیشنتصویر کی سائزتصویر کے پس منظر کی ترتیبات صرف پیشہ ورانہ ورژن کے لئے لائیو موڈ میں دستیاب ہیں. آپ اسے "ابھی موجودہ سٹائل" موڈ میں ٹیسٹ کر سکتے ہیں، لیکن یہ محفوظ نہیں کیا جائے گا.میراثداخلہ کے میدانانٹرویو سٹائل سکرپٹانسٹاایتالیائیٹیگ کے رنگٹیگ کیا گیا ہے Font SizeLabels ترتیباتلائسنسلائن کی اونچائیبائیں رنگہاؤس ہاؤس رنگچپ رہنے کا نظاملائیوپرنٹ سٹائل میں Tag کےانٹرویو سٹائل داخلہ Tag اس صفحے پرچیک باکس اشیاء کو ایک لائن پر بنائیںمکمل طور پر پھیلاؤ ہے؟انٹرویو فیلڈ مکمل وسیع بنائیں؟Radiobutton اشیاء کو ایک لائن پر بنائیںمارکیٹموبائل دیکھیںنہیں نہیںکوئی رابطہ فارم 7 اشیاء کے لئے پیش نظارہکوئی فارم اسٹائل ڈیزائن کے ساتھ سٹائل نہیں ہے، اس فارم کے لئے موجودہ ڈیزائن کو لاگو کرنے کے لئے "آج کے فارم کے لئے سٹائل فعال کریں" بٹن پر کلک کریں.کوئی پلگ ان منتخب نہیں کیا گیا انسٹال کرنے کے لئےکوئی نمونہ منتخب نہیں کیا گیامعمولیبلیکچیک باکس اور ریڈیو بٹونز کے لئے ایک لائن سٹائل لائیو موڈ میں صرف پیشہ ور ورانہ ورژن کے لئے. آپ اسے "ابھی موجودہ سٹائل" موڈ میں ٹیسٹ کر سکتے ہیں، لیکن یہ محفوظ نہیں کیا جائے گا.ناخوشگوارکھولیں سٹائلاکاؤنٹ دیکھنے کے لئے opt-inاصل سائزآؤٹ لائنباہر نکلناپڈنگبراہ مہربانی داخل کریں سٹائل سکرپٹ عنوانپیشگوئیغیر معمولی پیش نظرپیشگوئی کا طریقہریڈیودو بار تکرار کریںافقی طور پر تکرار کریںورٹیکاٹ کی تکراردوبارہ ڈاؤن لوڈ کریں defaultریڈنگبچاؤبچاؤدوسری سٹائل کا منظراس فارم کے لئے سٹائل سکرپٹ کا انتخاب کریںترتیباتترتیبات کو محفوظ کریں جیسےسایہ ہےسایہ کا رنگسایہ کی پوزیشنمضبوطیکچھ موضوعات یا صفحہ بنانے والے (Fe. ٹریو آرکیٹیکٹ، Optimizepress وغیرہ انٹرویو سٹائل کو داخل کر سکتے ہیں ٹیگ کریں لوڈنگ سٹائل کے نظام کے اندر اندر ٹیگ اس مسئلے کو درست کرے گا اور آپ کے فارم سٹائل دکھائیں گے.تقسیم کا طریقہتقسیم نظرراجکماریشروع کرنے کے لئے سٹائلڈیفالٹ سٹائل سکرپٹ کی تخلیق سے شروع کریںآپ کی تخلیق کے ساتھ شروع سب سے پہلے رابطہ نمبر 7 تمام پیشہ ورانہ خصوصیات کے ساتھ آپ کے 14 دن مفت ٹیسٹ شروع کریں یہاں یہاں ہے !مرحلہ 1 کلک کریں "Edit with thrive architect" لنک میں ایڈمن صفحہ فہرستمرحلہ 2 "Settings" آیکون پر دائیں ورٹک مینو پر کلک کریںمرحلہ 3 "مزید ترتیبات" => "CSS میں سیکشن »مرحلہ 4 اس بات کو یقینی بنائیں کہ "CSS سے نکالنے کے لئے نہیں چیک کیا گیا ہےمرحلہ 5 "Save Work" کے بٹن پر کلک کریںسٹائل تمام فارم ایک کلک میں عالمی سطح پرسٹائل ہر شکل کے ساتھ کسی بھی سٹائل ڈیزائن انفرادی طور پرسٹائل سکرپٹ تمام شکلوں کے لئے غیر فعال ہےاس شکل کے لئے سٹائل سکرپٹ غیر فعال ہےاس فارم کے لئے سٹائل سکرپٹ کی اجازتنمونے کا انتخاب نہیں کیا گیا ہےسٹائل کی فہرستسٹائل سکرپٹ پیش نظرسٹائل سٹائل سٹائلسٹائل میں شامل کیا جائے گا Tag کےسٹائل میں شامل کیا جائے گا Tag کےکامیابی کےکی حمایت & KBٹیبلٹ ویڈیوزمتن کا رنگاس سٹائل کے نظام کو عالمی سطح پر تمام شکلوں کے لئے ممکن کیا گیا ہے. اگر آپ رابطہ فارم کے لئے آپ کے موضوع کی سٹائل کا استعمال کرنا چاہتے ہیں تو 7 کلک کریں "غائب" بٹن.اس طرح کے طور پر اس طرح کے طور پر % کے s عالمی سطح پر، آپ اسے موجودہ سٹائل کے ساتھ سٹائل کر سکتے ہیں.اس طرح کے طور پر اس طرح کے طور پر % کے s , آپ اسے موجودہ سٹائل کے ساتھ سٹائل کر سکتے ہیں.یہ فارم موجودہ سٹائل کے ساتھ سٹائل کیا جاتا ہے، آپ اسے غیر فعال کر سکتے ہیں اور عالمی ترتیبات کا استعمال کرسکتے ہیں.یہ فارم کسی بھی سٹائل ڈیزائن کے ساتھ سٹائل نہیں ہے، آپ اس فارم کے لئے موجودہ ڈیزائن کو فعال کر سکتے ہیں یا "تمام فارم کے لئے استعمال کریں" پر کلک کرکے عالمی سٹائل ڈیزائن کو ترتیب دے سکتے ہیں.یہ نظام موجود نہیں ہےٹوبیاس کانڈٹویٹرنگغیر معمولیپرو کے لئے اپ گریڈ کریںتصویر اپ لوڈ کریںورٹیکا طویلWOW سٹائل رابطہ فارم 7خوش آمدیدوسیعجی ہاںآپ موجودہ سٹائل کی ترتیبات کے ساتھ نئے سٹائل ڈیزائن بن سکتے ہیں. اگر آپ مندرجہ ذیل میں سفید سٹائل Schem Unchek Checkbox بنانا چاہتے ہیں.آپ کو حذف نہیں کر سکتےآپ اس صفحے کی قسم پر اس ترتیب کا استعمال نہیں کرسکتے ہیںآپ کے پاس کوئی رابطہ فارم نہیں ہے 7 اشیاءآپ کو رابطہ فارم 7 کے لئے کسی بھی سٹائل ڈیزائن کا استعمال نہیں کرتے ہیں. کلک کریں "تمام فارم کے لئے استعمال کریں" موجودہ نظام کو عالمی سطح پر استعمال کرنے کے لئے.حذف کریںتمام اقسام کے لئے دستیاب↑ انگریزی ویکیپیڈیا کے مشارکین۔ //Saleswonder.BizHttps: //Saleswonder.Biz/Blog/4Free-Contact-Form-7-Cf7-Formular-Und-Klick-Tipp-Einfach-Verbinden/languages/cf7-styler-ru_RU.po000064400000123142147600046700012112 0ustar00msgid "" msgstr "" "Language: ru-RU\n" "POT-Creation-Date: 2020-02-06 11:35+0000\n" "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" "PO-Revision-Date: 2023-04-15 16:06+0000\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: WOW Style Contact Form 7\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: Russian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Loco-Version: 2.3.3; wp-5.4" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- отключить схема стиль" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "- выберите -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14-дневный пробный" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s , используемых для всех форм глобально, нажмите кнопку " "\"Использовать для всех форм\", Если вы хотите использовать текущую схему." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Активировать стиль для всех форм*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Активировать стиль для текущей формы" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "В качестве альтернативы вы можете отключить очистить стили, как на " "процветать архитектор:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Цвет Фона" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Фоновое Изображение" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Перед использованием ух контактный стиль Форма 7, вам нужно установить и " "активировать контактную форму 7 плагин." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "БГ цвет" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "БГ непрозрачность" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Радиус размытия " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "Границы" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Цвет Границы" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Радиус Границы" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Тип Границы" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "Ширина Границы " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "Кнопки Границы" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Кнопки" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Такаюки Миеси" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "Отменить" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "CF7 Настройщик требует контактная форма 7 плагин должен быть установлен и " "активен. Вы можете скачать %ов." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "CF7 форма Не выбрана" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7 Стайлер" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Чекбоксы & Переключатели" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "Чистые Стили" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "Понятно" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "Рядом" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Цвет" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Контактная форма 7 список" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "Содержать" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Скопировать текущие настройки схема стиль " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "Крышка" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "Создать" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Создать несколько схем " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Создать новую схему стиль!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Текущий Стиль" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "В настоящее время " "виде выполнен с %- " "ОВ. Как в бесплатной версии вы можете разработать только одну форму " "одновременно, и если вы активируете стиль для текущей форме, будут удалены " "от других форма." #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "В настоящее время виде выполнен с %- ОВ." " Как в бесплатной версии вы можете разработать только одну форму " "одновременно, и если вы активируете стиль для текущей форме, будут удалены " "от других форма." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "Пользовательские CSS" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "Пользовательский код CSS " #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Накатал" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Схема По Умолчанию " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "Удалить Изображения" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "удален" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Вид Рабочего Стола " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Отключить стиль для всех форм" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Отключить стиль для текущей формы" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "Вам нравится получать премиум-версию Styler бесплатно? Затем Введите свою лицензию " "WP2LEADS pro или получите лицензию href=\"https://wp2leads-for-klick-" "tipp.com/web/go-pro-plus-get-all-done4u/\" target=\"_blank\">здесь !" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "Вы хотите сохранить изменения, прежде чем покинуть страницу?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "Пунктирной" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Двойной" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Дублировать форму во втором столбце" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "включен для всех форм" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Ошибка" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Выход Из Полноэкранного Режима" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Семейство Шрифтов " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "Размер Шрифта " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Стиль Шрифта " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "Вес Шрифта " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Форма БГ изображения и цвета" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Настройка Форма " #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Форма Не выбрана" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Форма Заполнения, Поля И Границы" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Текст Форма " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "Полном Экране" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "ПАЗ" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Горизонтальная Длина" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Наведите БГ цвет" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "При Наведении Цвет" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Цвет Текста При Наведении" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Как превратить вашу контактную форму form7 в конвертации и простое в " "использовании и профессиональный разработанный контактная форма, \"опрос\" " "генератора лидов или привлекательные формы" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "href=\"https://wp2leads-for-klick-tipp.com/web/go-pro-plus-get-all-done4u/\" " "target=\"_blank\">" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "Я довольна этим! Сохранить" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Если вы используете компоновщики страниц, такие как Thrive Architect, " "OptimizePress и т. Д., Посетите нашу страницу базы знаний для " "устранение возможных проблем" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Если вам нужно загрузить схемы внутри тега<body> тег " "только на некоторых страницах, вы можете сделать это на frontend с " "использованием \"CF7 Стайлер\" кнопку. Эта функция доступна только для " "одного типа поста (страницы, посты, продукты и т. д.) и в премиум версии. Вы " "не можете сделать это на страницах архивов (блог, список продуктов и т. д.) " "В этом случае вы должны использовать глобальные параметры." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Если вам нужно загрузить схемы внутри тега<body> тег " "только на некоторых страницах, вы можете сделать это на frontend с " "использованием \"CF7 Стайлер\" кнопку. Эта функция доступна только для " "одного типа поста (страницы, посты, продукты и т. д.). Вы не можете сделать " "это на страницах архивов (блог, список продуктов и т. д.) В этом случае вы " "должны использовать глобальные параметры." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "Настройки фонового изображения доступна в режиме Live только для " "профессиональной версии. Вы можете протестировать его в режиме " "предварительного просмотра \"текущий стиль\", но он не будет сохранен." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Непрозрачность Изображения " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Положение Изображения " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "Размер Изображения " #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Наследовать" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Поля Ввода" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Введите название схемы в стиле " #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Врезные" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Курсив" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Ярлыки Цвет" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Размер Шрифта Надписей " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Настройки Метки " #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Лицензия" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "Линия Высота" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Цвет Ссылки " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Ссылки При Наведении Цвет" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Параметры Ссылки " #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "Жить" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Загрузить стили в тегах<Body> тег" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "" "Загрузить стили внутри тега<body> тег на этой странице" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Сделать элемент флажок одному в строке" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "Сделать полную ширину?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "Сделайте поля для ввода ширины?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Сделать элемент управления RadioButton одному в строке" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Маржа" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Мобильный Вид" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "Нет" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "Нет контактной формы 7 предметов для предварительного просмотра" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "Нет формы выполнен с схема стиль, нажмите кнопку \"Активировать стиль для " "текущей формы кнопки\" Применить текущие схемы для этой формы." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "Нет плагин выбран для установки" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "Нет схемы выбранный стиль" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "Нормальный" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Косой" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "Один за стили линий для галочки и переключатели в режиме Live только для " "профессиональной версии. Вы можете протестировать его в режиме " "предварительного просмотра \"текущий стиль\", но он не будет сохранен." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Непрозрачность" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Откройте стайлер" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "Оригинальный размер" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Контур" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "Прежде всего" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Обивка" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Пожалуйста, введите название схемы в стиле " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Предварительный просмотр" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "Режим предварительного просмотра " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Предварительный Просмотр Нестилизованный" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Радиус" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "Повторите оба" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "Повторите горизонтально" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "Повторить вертикальный" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Сброс по умолчанию" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Хребет" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "Сохранить" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "Спас" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Второй вид колонки " #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Выберите схему стиль для этой формы" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Параметры" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "Настройки сохраняются как " #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "Тень" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "Цвет Тени" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Теневой Позиции" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Твердые" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Некоторые темы или строителей странице (Фе. Процветать архитектор, " "OptimizePress и т. д.) можно удалить встроенные стили внутри тег<" "head> тег. Загрузка схемы в пределах <тело> тег " "будет исправить эту проблему и показать свои формы в стиле." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "Режим разделения " #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "Сплит вид" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Радиус распространения" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Начать укладку" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Начните с создания схемы стиль по умолчанию " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Начните с создания вашего первого " "контактная форма 7" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "Начать свой 14-дневную бесплатную пробную версию со всеми профессиональными " "функциями здесь!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Шаг 1. Нажмите кнопку \"Редактировать процветать архитектор ссылке\" в " "списке страниц админ " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "Шаг 2. Нажмите кнопку \"Настройки\" значок на правом вертикальном меню" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" "Шаг 3. \"Дополнительные настройки\" => \"CSS в разделе <head>\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "Шаг 4. Убедитесь, что \"не полосы CSS от <руководитель>\" проверяется" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Шаг 5. Нажмите кнопку \"Сохранить\" кнопку" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "Стиль всех форм в один клик во всем мире" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "Стиль каждой формы с любым схема стиля индивидуально" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Схема стиль отключен для всех форм" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Схема стиль отключена для этой формы" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Схема стиль с поддержкой для этой формы" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "Схема стиль Не выбран" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "Список схем " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Схемы предварительный просмотр стилей" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "Параметры схем " #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "Стиль будет загружен в тег" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "Стиль будет загружен в тег" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Успех" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Вид Планшета " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Цвет Текста" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Эта форма выполнена с %- ОВ по всему миру, вы можете стиль " "его с текущим стилем схеме." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Эта форма выполнена с %- ОВ, вы можете стиль его с текущим " "стилем схеме." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Эта форма выполнена с текущей схемой стиле, вы можете отключить его и " "использовать глобальные параметры." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Эта форма не стилизовано под какую-то схему, стиль, вы можете включить ток " "схема данной формы или создавать глобальные схемы стиль, нажав кнопку " "\"Использовать для всех форм\" под заголовком программы." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Эта схема не существовала" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Эта схема стиль глобально для всех форм. Если вы хотите использовать стиль " "вашей темы для контактный формуляр 7 Нажмите кнопку \"Отключить\"." #. Author of the plugin msgid "Tobias Conrad" msgstr "Тобиас Конрад" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "Учебник" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Нестилизованный" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "Обновление до Pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Загрузить Изображение" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "Вертикальная Длина" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Добро пожаловать" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "Ширина" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "Вау контакт стиль Форма 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "Да" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "Вы можете создать новую схему в стиле с текущими настройками схемы. Если вы " "хотите создать флажок стиль unchek счем ниже." #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "Вы не можете удалить" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "Вы не можете использовать эти настройки на этого типа страницы " #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "Вы не имеете никакого контактная форма 7 предметов" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "Вы не используете какой-либо из схем для контактный формуляр 7. Нажмите " "кнопку \"Использовать для всех форм\" использовать текущую схему в " "глобальном масштабе." languages/cf7-styler-ru_RU.mo000064400000060433147600046700012112 0ustar00  m m  P    ! ^2    ^a z " / HVq DO_f u  0 7= N Z d o{    1fO ft ) 1>W] dq  ,=Cct" #zj[ c o}   &+1!Dfo     (TOBL 4 @ F! L!%m!2!#!#!""1"N"a"w"""""" " ""bv#Y#Z3$$8% S%a%j%s% %%%%%%|%G&/Z&(&q&%'-'`C''*)) ))=*D"+g+ ,!,=,%O,u,C-b-q----- -.."../1/Q/i/ x//.//O/ 0+0+:00f0002$ 4,24_4#n4%4#464>5sR5o667K7BZ7 797"78"8;84P88;888909'79_9"~9/9R90$;RU;<|?4.B*cB$BfBD1D9EDD DD+DDD E%E/%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Language: ru-RU POT-Creation-Date: 2020-02-06 11:35+0000 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); PO-Revision-Date: 2023-04-15 16:06+0000 X-Generator: Loco https://localise.biz/ Project-Id-Version: WOW Style Contact Form 7 Report-Msgid-Bugs-To: Last-Translator: Language-Team: Russian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Loco-Version: 2.3.3; wp-5.4- отключить схема стиль- выберите -14-дневный пробный%s , используемых для всех форм глобально, нажмите кнопку "Использовать для всех форм", Если вы хотите использовать текущую схему.Активировать стиль для всех форм*Активировать стиль для текущей формыВ качестве альтернативы вы можете отключить очистить стили, как на процветать архитектор:БГ цветБГ непрозрачностьЦвет ФонаФоновое ИзображениеПеред использованием ух контактный стиль Форма 7, вам нужно установить и активировать контактную форму 7 плагин.Радиус размытия ГраницыЦвет ГраницыРадиус ГраницыТип ГраницыШирина Границы Кнопки ГраницыКнопкиТакаюки МиесиCF7 Настройщик требует контактная форма 7 плагин должен быть установлен и активен. Вы можете скачать %ов.CF7 форма Не выбранаCF7 СтайлерОтменитьЧекбоксы & ПереключателиЧистые СтилиПонятноРядомЦветКонтактная форма 7 списокСодержатьСкопировать текущие настройки схема стиль КрышкаСоздатьСоздать несколько схем Создать новую схему стиль!Текущий СтильВ настоящее время виде выполнен с %- ОВ. Как в бесплатной версии вы можете разработать только одну форму одновременно, и если вы активируете стиль для текущей форме, будут удалены от других форма.В настоящее время виде выполнен с %- ОВ. Как в бесплатной версии вы можете разработать только одну форму одновременно, и если вы активируете стиль для текущей форме, будут удалены от других форма.Пользовательские CSSПользовательский код CSS НакаталСхема По Умолчанию Удалить ИзображенияВид Рабочего Стола Отключить стиль для всех формОтключить стиль для текущей формыВам нравится получать премиум-версию Styler бесплатно? Затем Введите свою лицензию WP2LEADS pro или получите лицензию href="https://wp2leads-for-klick-tipp.com/web/go-pro-plus-get-all-done4u/" target="_blank">здесь !Вы хотите сохранить изменения, прежде чем покинуть страницу?ПунктирнойДвойнойДублировать форму во втором столбцеОшибкаВыход Из Полноэкранного РежимаСемейство Шрифтов Размер Шрифта Стиль Шрифта Вес Шрифта Форма БГ изображения и цветаНастройка Форма Форма Заполнения, Поля И ГраницыТекст Форма Форма Не выбранаПолном ЭкранеПАЗГоризонтальная ДлинаНаведите БГ цветПри Наведении ЦветЦвет Текста При НаведенииКак превратить вашу контактную форму form7 в конвертации и простое в использовании и профессиональный разработанный контактная форма, "опрос" генератора лидов или привлекательные формыЯ довольна этим! СохранитьЕсли вы используете компоновщики страниц, такие как Thrive Architect, OptimizePress и т. Д., Посетите нашу страницу базы знаний для устранение возможных проблемЕсли вам нужно загрузить схемы внутри тега<body> тег только на некоторых страницах, вы можете сделать это на frontend с использованием "CF7 Стайлер" кнопку. Эта функция доступна только для одного типа поста (страницы, посты, продукты и т. д.) и в премиум версии. Вы не можете сделать это на страницах архивов (блог, список продуктов и т. д.) В этом случае вы должны использовать глобальные параметры.Если вам нужно загрузить схемы внутри тега<body> тег только на некоторых страницах, вы можете сделать это на frontend с использованием "CF7 Стайлер" кнопку. Эта функция доступна только для одного типа поста (страницы, посты, продукты и т. д.). Вы не можете сделать это на страницах архивов (блог, список продуктов и т. д.) В этом случае вы должны использовать глобальные параметры.Непрозрачность Изображения Положение Изображения Размер Изображения Настройки фонового изображения доступна в режиме Live только для профессиональной версии. Вы можете протестировать его в режиме предварительного просмотра "текущий стиль", но он не будет сохранен.НаследоватьПоля ВводаВведите название схемы в стиле ВрезныеКурсивЯрлыки ЦветРазмер Шрифта Надписей Настройки Метки ЛицензияЛиния ВысотаЦвет Ссылки Ссылки При Наведении ЦветПараметры Ссылки ЖитьЗагрузить стили в тегах<Body> тегЗагрузить стили внутри тега<body> тег на этой страницеСделать элемент флажок одному в строкеСделать полную ширину?Сделайте поля для ввода ширины?Сделать элемент управления RadioButton одному в строкеМаржаМобильный ВидНетНет контактной формы 7 предметов для предварительного просмотраНет формы выполнен с схема стиль, нажмите кнопку "Активировать стиль для текущей формы кнопки" Применить текущие схемы для этой формы.Нет плагин выбран для установкиНет схемы выбранный стильНормальныйКосойОдин за стили линий для галочки и переключатели в режиме Live только для профессиональной версии. Вы можете протестировать его в режиме предварительного просмотра "текущий стиль", но он не будет сохранен.НепрозрачностьОткройте стайлерОригинальный размерКонтурПрежде всегоОбивкаПожалуйста, введите название схемы в стиле Предварительный просмотрПредварительный Просмотр НестилизованныйРежим предварительного просмотра РадиусПовторите обаПовторите горизонтальноПовторить вертикальныйСброс по умолчаниюХребетСохранитьСпасВторой вид колонки Выберите схему стиль для этой формыПараметрыНастройки сохраняются как ТеньЦвет ТениТеневой ПозицииТвердыеНекоторые темы или строителей странице (Фе. Процветать архитектор, OptimizePress и т. д.) можно удалить встроенные стили внутри тег<head> тег. Загрузка схемы в пределах <тело> тег будет исправить эту проблему и показать свои формы в стиле.Режим разделения Сплит видРадиус распространенияНачать укладкуНачните с создания схемы стиль по умолчанию Начните с создания вашего первого контактная форма 7Начать свой 14-дневную бесплатную пробную версию со всеми профессиональными функциями здесь!Шаг 1. Нажмите кнопку "Редактировать процветать архитектор ссылке" в списке страниц админ Шаг 2. Нажмите кнопку "Настройки" значок на правом вертикальном менюШаг 3. "Дополнительные настройки" => "CSS в разделе <head>"Шаг 4. Убедитесь, что "не полосы CSS от <руководитель>" проверяетсяШаг 5. Нажмите кнопку "Сохранить" кнопкуСтиль всех форм в один клик во всем миреСтиль каждой формы с любым схема стиля индивидуальноСхема стиль отключен для всех формСхема стиль отключена для этой формыСхема стиль с поддержкой для этой формыСхема стиль Не выбранСписок схем Схемы предварительный просмотр стилейПараметры схем Стиль будет загружен в тегСтиль будет загружен в тегУспехВид Планшета Цвет ТекстаЭта схема стиль глобально для всех форм. Если вы хотите использовать стиль вашей темы для контактный формуляр 7 Нажмите кнопку "Отключить".Эта форма выполнена с %- ОВ по всему миру, вы можете стиль его с текущим стилем схеме.Эта форма выполнена с %- ОВ, вы можете стиль его с текущим стилем схеме.Эта форма выполнена с текущей схемой стиле, вы можете отключить его и использовать глобальные параметры.Эта форма не стилизовано под какую-то схему, стиль, вы можете включить ток схема данной формы или создавать глобальные схемы стиль, нажав кнопку "Использовать для всех форм" под заголовком программы.Эта схема не существовалаТобиас КонрадУчебникНестилизованныйОбновление до ProЗагрузить ИзображениеВертикальная ДлинаВау контакт стиль Форма 7Добро пожаловатьШиринаДаВы можете создать новую схему в стиле с текущими настройками схемы. Если вы хотите создать флажок стиль unchek счем ниже.Вы не можете удалитьВы не можете использовать эти настройки на этого типа страницы Вы не имеете никакого контактная форма 7 предметовВы не используете какой-либо из схем для контактный формуляр 7. Нажмите кнопку "Использовать для всех форм" использовать текущую схему в глобальном масштабе.удаленвключен для всех формhref="https://wp2leads-for-klick-tipp.com/web/go-pro-plus-get-all-done4u/" target="_blank">languages/cf7-styler-pt_BR.po000064400000110665147600046700012072 0ustar00msgid "" msgstr "" "Language: pt-BR\n" "POT-Creation-Date: 2020-02-06 11:35+0000\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "PO-Revision-Date: 2023-04-15 16:06+0000\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: WOW Style Contact Form 7\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: Portuguese (Brazil)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Loco-Version: 2.3.3; wp-5.4" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- desative o estilo do esquema" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "- selecione -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14 dias de período Experimental" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s usados para todas as formas globalmente, clique em " "\"Usar de todas as formas\" se você quiser usar o Esquema atual." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Activar estilo para todas as formas*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Activar estilo para a forma atual" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "Como alternativa, você pode desativar limpar os estilos, como em Prosperar " "arquiteto:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Cor De Fundo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Imagem De Fundo" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Antes de utilizar WOW Estilo Formulário de Contato 7, você precisará " "instalar e ativar o Formulário de Contato 7 plugin." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "VERSO a Cores" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "BG Opacidade" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Blur radius" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "Fronteira" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Cor Da Borda" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Border Radius" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Tipo De Borda" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "Largura Da Borda" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "Botão De Fronteira" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Botões" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Por Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "Cancelar" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "CF7 Personalizador de sistemas requer o Formulário de Contato 7 plugin para " "ser instalado e ativo. Você pode fazer o download %s." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "CF7 Formulário não está seleccionada" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7 Styler" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Caixas & Radiobuttons" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "Limpar Estilos" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "Claro" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "Fechar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Cor" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Formulário de contato 7 lista de" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "Conter" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Copiar um estilo atual esquema de configurações" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "Cobertura" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "Criar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Criar vários esquemas de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Criar novo estilo de esquema!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Estilo Atual" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "Actualmente %s " "formulário é denominado com " "%s. Como na versão gratuita, você pode estilo apenas uma forma de " "cada vez e se você ativar o estilo para a forma atual, o estilo vai ser " "removido de outra forma." #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "Actualmente %s formulário é denominado com " "%s. Como na versão gratuita, você pode estilo apenas uma forma de " "cada vez e se você ativar o estilo para a forma atual, o estilo vai ser " "removido de outra forma." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "CSS personalizado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "CSS personalizado Código" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Tracejada" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Esquema Padrão" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "Apagar Imagem" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "excluídos" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Visualização Da Área De Trabalho" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Desativar estilo para todas as formas" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Desativar o estilo para a forma atual" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "¿Te gusta obtener la versión premium de styler gratis? Luego, ingrese su licencia " "WP2LEADS pro u obtenga una licencia aquí !" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "Você deseja salvar as alterações antes de sair da página?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "Pontilhada" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Duplo" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Formulário duplicados em segunda coluna" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "habilitado para todas as formas" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Erro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Saída De Ecrã Inteiro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Família De Tipos De Letra" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "Tamanho Da Fonte" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Estilo Da Fonte" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "Peso Da Fonte" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Formulário BG Image & Cores" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Formulário De Personalização" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Formulário não está seleccionada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Formulário De Preenchimento, De Margem E De Fronteiras" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Texto De Forma" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "Tela Cheia" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "Groove" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Horizontal De Comprimento" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Passe o mouse VERSO a Cores" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "Passe O Mouse A Cor" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Passe O Mouse A Cor Do Texto" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Como transformar o seu contato form7 forma em uma conversão e fácil de usar " "e pro estilo formulário de contato, \"pesquisa\" gerador de chumbo ou um " "olho captura formulário" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "https://saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "Eu estou feliz com isso! Salvar" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Si está utilizando creadores de páginas, como Thrive Architect, " "OptimizePress, etc., consulte nuestra página de base de conocimiento " "para arreglando posibles problemas" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Se você precisar de carregar o estilo esquema dentro do <body>" " tag apenas em algumas páginas, você pode fazê-lo no frontend usando " "\"CF7 Styler\" botão. Esta função só está disponível para um único post " "types (páginas, posts, produtos etc.) e na versão premium. Você não pode " "fazê-lo em arquivos de páginas (blog, lista de produtos, etc), neste caso, " "você precisa usar configurações globais." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Se você precisar de carregar o estilo esquema dentro do <body>" " tag apenas em algumas páginas, você pode fazê-lo no frontend usando " "\"CF7 Styler\" botão. Esta função só está disponível para um único post " "types (páginas, posts, produtos, etc.). Você não pode fazê-lo em arquivos de " "páginas (blog, lista de produtos, etc), neste caso, você precisa usar " "configurações globais." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "Imagem de fundo definições disponíveis no modo de viver apenas para a versão " "Professional. Você pode testá-lo no modo de visualização atual \"estilo\", " "mas ele não será salvo." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Opacidade Da Imagem" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Posição Da Imagem" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "Tamanho Da Imagem" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Herdam" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Campos De Entrada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Estilo de entrada do esquema de título" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Inserção" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Itálico" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Rótulos De Cor" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Etiquetas Tamanho Da Fonte" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Etiquetas De Configurações" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Licença" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "Altura Da Linha" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Cor Dos Links" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Links Passe A Cor" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Configurações De Links" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "Ao vivo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Carregar estilos de no <body> tag" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "" "Carga estilos dentro do <body> tag sobre esta página" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Fazer a seleção de um item por linha" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "Fazer toda a largura?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "Fazer campos de entrada de largura total?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Fazer radiobutton de um item por linha" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Margem" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Modo De Exibição Móvel" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "NENHUM" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "Não Formulário de Contato 7 itens para visualização" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "De qualquer forma é decorado com estilo de esquema, clique em \"Ativar " "estilo para a forma atual\" botão para aplicar o esquema atual para este " "formulário." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "Nenhum plugin selecionado para instalar" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "Sem estilo esquema selecionado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "Normal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Oblíqua" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "Um por linha estilos para caixas de verificação e radiobuttons no modo de " "viver apenas para a versão Professional. Você pode testá-lo no modo de " "visualização atual \"estilo\", mas ele não será salvo." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Opacidade" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Abrir styler" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "Optar por ver a conta" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "Tamanho Original" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Estrutura de tópicos" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "Início" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Preenchimento" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Por favor, introduza o estilo esquema de título" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Pré-visualização" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "Modo de pré-visualização" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Pré-Visualização Sem Estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Raio" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "Repita as" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "Repita horizontal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "Repita vertical" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Redefinir para o padrão" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Ridge" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "Salvar" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "Salvo" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Segundo a coluna de vista" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Selecione o estilo de esquema para este formulário" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Definições" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "Configurações salvas como " #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "Sombra" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "A Cor Da Sombra" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Sombra Posição" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Sólidos" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Alguns temas ou página de construtores (fe. Prosperar Arquiteto, " "OptimizePress etc.) pode remover estilos inline dentro de <head>" " tag. Carregamento de estilo esquema dentro do <body>" " tag irá corrigir este problema e mostrar as suas formas de estilo." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "O modo Split" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "Split view" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Propagação do raio" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Início de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Começar com a criação de Estilo Padrão do Regime de" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Começar com a criação de seu primeiro " "Formulário de Contato 7" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "Inicie seu teste gratuito de 14 dias com todas as funções Profissionais aqui!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Passo 1. Clique em \"Editar com Prosperar arquiteto\" link na página de " "administração de lista" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "Passo 2. Clique em \"Configurações\" ícone no direito de menu vertical" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" "Passo 3. \"Configurações avançadas\" => \"CSS na seção <head>\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "Passo 4. Certifique-se de que \"não extrair CSS a partir de <head>\" " "está marcada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Passo 5. Clique em \"SALVAR o TRABALHO\" botão" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "O estilo de todas as formas em um clique globalmente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "" "Estilo de cada um formulário com qualquer estilo de esquema individualmente" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Estilo esquema desabilitado para todas as formas" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Estilo esquema desabilitado para este formulário" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Estilo regime habilitado para este formulário" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "Estilo de esquema não é selecionado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "Estilo regimes lista" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Estilo de esquemas de pré-visualização" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "Estilo esquemas de configurações" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "O estilo vai ser carregado em marca" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "O estilo vai ser carregado em marca" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Sucesso" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "Suporte e KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Tablet Vista" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Cor Do Texto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Este formulário é denominado com %s no mundo, você pode " "personalizá-la com Estilo atual regime." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Este formulário é denominado com %s, você pode personalizá-" "la com Estilo atual regime." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Este formulário está decorado com um Estilo atual Regime, você pode desativá-" "lo e usar configurações globais." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Este formulário não decorados com qualquer Estilo de esquema, você pode " "habilitar o Esquema atual para este formulário ou conjunto global de Estilo " "de Esquema clicando em \"Usar de todas as formas\" abaixo, o esquema de " "título." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Este esquema não existiu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Este Estilo Esquema ativado globalmente para todas as formas. Se você quiser " "usar o tema do estilo para o Formulário de Contato 7 clique em botão " "\"Desabilitar\"." #. Author of the plugin msgid "Tobias Conrad" msgstr "Tobias Conrad" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "Tutorial" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Sem estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "A atualização para o Pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Upload De Imagem" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "A Extensão Vertical" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Bem-vindo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "Largura" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "WOW Estilo Formulário de Contato 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "SIM" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "Você pode criar novo estilo de esquema com o atual esquema de configurações. " "Se você deseja criar em branco estilo schem unchek caixa de seleção abaixo." #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "Você não pode apagar" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "Você não pode usar esse definições deste tipo de página" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "Você não tem nenhum Formulário de Contato 7 itens" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "Você não utilizar qualquer Estilo de Esquemas para Formulário de Contato 7. " "Clique em \"Usar de todas as formas\" usar atual Esquema global." languages/cf7-styler-pt_BR.mo000064400000046324147600046700012067 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'() ) )*$*!*V* %+ 3+ @+M+|]+ + + + + ,,*,>,F,[,', ---5-D-J-Q-!U-w-1~- -- -- -< .I/>0P0 j0t0 0#0%0%01="2 `2k2(q222222 2 3"37B3z3#3 33333 4&44457.9B9V9h9 :':'9: a:l:u::::: :::;4;HL;&;;);&;#<*<D<7K<<' =H=g=n=w= F> P>]>s>>> >0>>>?0? 5???Q?a?z????3? ??@ @@-@!6@ XA eApAA7AhA8B_JCGCICW%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Language: pt-BR POT-Creation-Date: 2020-02-06 11:35+0000 Plural-Forms: nplurals=2; plural=n != 1; PO-Revision-Date: 2023-04-15 16:06+0000 X-Generator: Loco https://localise.biz/ Project-Id-Version: WOW Style Contact Form 7 Report-Msgid-Bugs-To: Last-Translator: Language-Team: Portuguese (Brazil) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Loco-Version: 2.3.3; wp-5.4- desative o estilo do esquema- selecione -14 dias de período Experimental%s usados para todas as formas globalmente, clique em "Usar de todas as formas" se você quiser usar o Esquema atual.Activar estilo para todas as formas*Activar estilo para a forma atualComo alternativa, você pode desativar limpar os estilos, como em Prosperar arquiteto:VERSO a CoresBG OpacidadeCor De FundoImagem De FundoAntes de utilizar WOW Estilo Formulário de Contato 7, você precisará instalar e ativar o Formulário de Contato 7 plugin.Blur radiusFronteiraCor Da BordaBorder RadiusTipo De BordaLargura Da BordaBotão De FronteiraBotõesPor Takayuki MiyoshiCF7 Personalizador de sistemas requer o Formulário de Contato 7 plugin para ser instalado e ativo. Você pode fazer o download %s.CF7 Formulário não está seleccionadaCF7 StylerCancelarCaixas & RadiobuttonsLimpar EstilosClaroFecharCorFormulário de contato 7 lista deConterCopiar um estilo atual esquema de configuraçõesCoberturaCriarCriar vários esquemas de estiloCriar novo estilo de esquema!Estilo AtualActualmente %s formulário é denominado com %s. Como na versão gratuita, você pode estilo apenas uma forma de cada vez e se você ativar o estilo para a forma atual, o estilo vai ser removido de outra forma.Actualmente %s formulário é denominado com %s. Como na versão gratuita, você pode estilo apenas uma forma de cada vez e se você ativar o estilo para a forma atual, o estilo vai ser removido de outra forma.CSS personalizadoCSS personalizado CódigoTracejadaEsquema PadrãoApagar ImagemVisualização Da Área De TrabalhoDesativar estilo para todas as formasDesativar o estilo para a forma atual¿Te gusta obtener la versión premium de styler gratis? Luego, ingrese su licencia WP2LEADS pro u obtenga una licencia aquí !Você deseja salvar as alterações antes de sair da página?PontilhadaDuploFormulário duplicados em segunda colunaErroSaída De Ecrã InteiroFamília De Tipos De LetraTamanho Da FonteEstilo Da FontePeso Da FonteFormulário BG Image & CoresFormulário De PersonalizaçãoFormulário De Preenchimento, De Margem E De FronteirasTexto De FormaFormulário não está seleccionadaTela CheiaGrooveHorizontal De ComprimentoPasse o mouse VERSO a CoresPasse O Mouse A CorPasse O Mouse A Cor Do TextoComo transformar o seu contato form7 forma em uma conversão e fácil de usar e pro estilo formulário de contato, "pesquisa" gerador de chumbo ou um olho captura formulárioEu estou feliz com isso! SalvarSi está utilizando creadores de páginas, como Thrive Architect, OptimizePress, etc., consulte nuestra página de base de conocimiento para arreglando posibles problemasSe você precisar de carregar o estilo esquema dentro do <body> tag apenas em algumas páginas, você pode fazê-lo no frontend usando "CF7 Styler" botão. Esta função só está disponível para um único post types (páginas, posts, produtos etc.) e na versão premium. Você não pode fazê-lo em arquivos de páginas (blog, lista de produtos, etc), neste caso, você precisa usar configurações globais.Se você precisar de carregar o estilo esquema dentro do <body> tag apenas em algumas páginas, você pode fazê-lo no frontend usando "CF7 Styler" botão. Esta função só está disponível para um único post types (páginas, posts, produtos, etc.). Você não pode fazê-lo em arquivos de páginas (blog, lista de produtos, etc), neste caso, você precisa usar configurações globais.Opacidade Da ImagemPosição Da ImagemTamanho Da ImagemImagem de fundo definições disponíveis no modo de viver apenas para a versão Professional. Você pode testá-lo no modo de visualização atual "estilo", mas ele não será salvo.HerdamCampos De EntradaEstilo de entrada do esquema de títuloInserçãoItálicoRótulos De CorEtiquetas Tamanho Da FonteEtiquetas De ConfiguraçõesLicençaAltura Da LinhaCor Dos LinksLinks Passe A CorConfigurações De LinksAo vivoCarregar estilos de no <body> tagCarga estilos dentro do <body> tag sobre esta páginaFazer a seleção de um item por linhaFazer toda a largura?Fazer campos de entrada de largura total?Fazer radiobutton de um item por linhaMargemModo De Exibição MóvelNENHUMNão Formulário de Contato 7 itens para visualizaçãoDe qualquer forma é decorado com estilo de esquema, clique em "Ativar estilo para a forma atual" botão para aplicar o esquema atual para este formulário.Nenhum plugin selecionado para instalarSem estilo esquema selecionadoNormalOblíquaUm por linha estilos para caixas de verificação e radiobuttons no modo de viver apenas para a versão Professional. Você pode testá-lo no modo de visualização atual "estilo", mas ele não será salvo.OpacidadeAbrir stylerOptar por ver a contaTamanho OriginalEstrutura de tópicosInícioPreenchimentoPor favor, introduza o estilo esquema de títuloPré-visualizaçãoPré-Visualização Sem EstiloModo de pré-visualizaçãoRaioRepita asRepita horizontalRepita verticalRedefinir para o padrãoRidgeSalvarSalvoSegundo a coluna de vistaSelecione o estilo de esquema para este formulárioDefiniçõesConfigurações salvas como SombraA Cor Da SombraSombra PosiçãoSólidosAlguns temas ou página de construtores (fe. Prosperar Arquiteto, OptimizePress etc.) pode remover estilos inline dentro de <head> tag. Carregamento de estilo esquema dentro do <body> tag irá corrigir este problema e mostrar as suas formas de estilo.O modo SplitSplit viewPropagação do raioInício de estiloComeçar com a criação de Estilo Padrão do Regime deComeçar com a criação de seu primeiro Formulário de Contato 7Inicie seu teste gratuito de 14 dias com todas as funções Profissionais aqui!Passo 1. Clique em "Editar com Prosperar arquiteto" link na página de administração de listaPasso 2. Clique em "Configurações" ícone no direito de menu verticalPasso 3. "Configurações avançadas" => "CSS na seção <head>"Passo 4. Certifique-se de que "não extrair CSS a partir de <head>" está marcadaPasso 5. Clique em "SALVAR o TRABALHO" botãoO estilo de todas as formas em um clique globalmenteEstilo de cada um formulário com qualquer estilo de esquema individualmenteEstilo esquema desabilitado para todas as formasEstilo esquema desabilitado para este formulárioEstilo regime habilitado para este formulárioEstilo de esquema não é selecionadoEstilo regimes listaEstilo de esquemas de pré-visualizaçãoEstilo esquemas de configuraçõesO estilo vai ser carregado em marcaO estilo vai ser carregado em marcaSucessoSuporte e KBTablet VistaCor Do TextoEste Estilo Esquema ativado globalmente para todas as formas. Se você quiser usar o tema do estilo para o Formulário de Contato 7 clique em botão "Desabilitar".Este formulário é denominado com %s no mundo, você pode personalizá-la com Estilo atual regime.Este formulário é denominado com %s, você pode personalizá-la com Estilo atual regime.Este formulário está decorado com um Estilo atual Regime, você pode desativá-lo e usar configurações globais.Este formulário não decorados com qualquer Estilo de esquema, você pode habilitar o Esquema atual para este formulário ou conjunto global de Estilo de Esquema clicando em "Usar de todas as formas" abaixo, o esquema de título.Este esquema não existiuTobias ConradTutorialSem estiloA atualização para o ProUpload De ImagemA Extensão VerticalWOW Estilo Formulário de Contato 7Bem-vindoLarguraSIMVocê pode criar novo estilo de esquema com o atual esquema de configurações. Se você deseja criar em branco estilo schem unchek caixa de seleção abaixo.Você não pode apagarVocê não pode usar esse definições deste tipo de páginaVocê não tem nenhum Formulário de Contato 7 itensVocê não utilizar qualquer Estilo de Esquemas para Formulário de Contato 7. Clique em "Usar de todas as formas" usar atual Esquema global.excluídoshabilitado para todas as formashttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/languages/cf7-styler.pot000064400000066270147600046700011254 0ustar00#, fuzzy msgid "" msgstr "" "Language: \n" "POT-Creation-Date: 2023-04-15 16:12+0000\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: Loco https://localise.biz/" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" #. Author of the plugin msgid "Tobias Conrad" msgstr "" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" languages/cf7-styler-pl_PL.po000064400000113631147600046700012066 0ustar00msgid "" msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: Wow style (ES)\n" "Language: pl-PL\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-04-18 12:29+0000\n" "PO-Revision-Date: 2023-04-15 16:04+0000\n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: Polish\n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- wyłącz szablon stylu -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "- wybierz -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14-dniowy okres próbny" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #| msgid "" #| "%s used for all forms globally, click \"Use for all forms\" if you want to " #| "use current Scheme." msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s używany dla wszystkich formularzy globalnie, kliknij " "\"Użyj dla wszystkich formularzy\", jeśli chcesz użyć bieżącego schematu." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Aktywuj styl do wszystkich formularzy*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Aktywuj styl dla aktualnego formularza" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "Opcjonalnie możesz wyłączyć czyszczenie stylów podobnie jak we wtyczce " "Thrive Architect:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Kolor tła" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Obraz tła" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Przed użyciem wtyczki WOW Style Contact Form 7, musisz zainstalować i " "aktywować wtyczkę Contact Form 7." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "Kolor tła" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "Przezroczystość tła" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Promień rozmycia" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "Obramowanie" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Kolor obramowania" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Promień obramowania" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Rodzaj obramowania" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "Szerokość obramowania" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "Obramowanie przycisku" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Przyciski" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Autor: Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "Anuluj" #: cf7-styler.php:129 msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "Wtyczka do modyfikacji stylów formularza Contact Form 7 wymaga " "zainstalowania i aktywacji wtyczki Contact Form 7. Możesz pobrać %s." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "Formularz CF7 nie jest wybrany." #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7 Styler" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Pola wielokrotnego i jednokrotnego wyboru" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "Wyczyść style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "Wyczyść" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "Zamknij" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Kolor" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Lista formularzy CF7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "Zmieść" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Skopiuj ustawienia bieżącego szablonu stylu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "Pokryj" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "Utwórz" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Utwórz wiele szablonów stylu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Utwórz nowy szablon stylu!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Aktualny styl" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "Obecnie %s formularz " "jest stylizowany za pomocą " "%s. Tak jak w wersji darmowej możesz stylizować tylko jeden " "formularz w tym samym czasie i jeśli aktywujesz styl dla bieżącego " "formularza, styl zostanie usunięty z innych formularzy." #: admin/class-cf7-customizer-admin.php:481 #| msgid "" #| "Currently %s form is styled with %s. As in free version you can style only " #| "one form at a time and if you activate style for current form, style will be " #| "removed from other form." msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "Aktualnie styl formularza %s jest skonfigurowany przy pomocy %s. Ponieważ w " "wersji darmowej w danym momencie możesz modyfikować styl tylko jednego " "formularza, jeśli aktywujesz styl dla aktualnego formularza, styl zostanie " "usunięty z innego formularza." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "Niestandardowy CSS" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "Niestandardowy kod CSS" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Kreskowane" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Szablon domyślny" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "Usuń obraz" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "usunięty" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Widok pulpitu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Wyłączenie stylu dla wszystkich formularzy" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Wyłącz styl dla aktualnego formularza" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "Czy chcesz otrzymać wersję premium stylera za darmo? Następnie Wprowadź swoją licencję " "WP2LEADS pro lub zdobądź licencję tutaj!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "Chcesz zapisać zmiany przed opuszczeniem strony?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "Kropkowane" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Podwójne" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Duplikuj formularz w drugiej kolumnie" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "włączony dla wszystkich formularzy" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Błąd" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Wyjdź z pełnego ekranu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Rodzina czcionek" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "Rozmiar czcionki" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Styl czcionki" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "Waga czcionki" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Obraz i kolory tła formularza" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Dostosowanie formularza" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Nie wybrano formularza" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Wypełnienie, margines i obramowanie formularza" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Tekst formularza" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "Pełny ekran" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "Wklęsłe" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Długość pozioma" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Kolor tła po najechaniu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "Kolor po najechaniu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Kolor tekstu po najechaniu" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Jak z formularza kontaktowego Contact Form 7 zrobić formularz kontaktowy, " "który nie tylko będzie miał przyciągający uwagę profesjonalny wygląd oraz " "będzie łatwy w użyciu, ale także będzie miał wysoki współczynnik konwersji i " " będzie generował zapytania o oferty." #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "https://saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "Podoba mi się! Zapisz" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Jeśli używasz page builderów, takich jak Thrive Architect, OptimizePress itp." ", sprawdź naszą bazę wiedzy, aby naprawić ewentualne błędy" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Jeśli potrzebujesz załadować schemat stylu wewnątrz <body>" " tagu tylko na niektórych stronach, możesz to zrobić na frontend za pomocą " "przycisku \"CF7 Styler\". Ta funkcja jest dostępna tylko dla pojedynczych " "typów postów (stron, postów, produktów itp.) i w wersji premium. Nie możesz " "tego zrobić na stronach archiwalnych (blog, lista produktów itp.) W tym " "przypadku musisz użyć ustawień globalnych." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Jeśli potrzebujesz załadować schemat stylu wewnątrz <body>" " tagu tylko na niektórych stronach, możesz to zrobić na frontend za pomocą " "przycisku \"CF7 Styler\". Ta funkcja jest dostępna tylko dla pojedynczych " "typów postów (stron, postów, produktów itp.). Nie możesz tego zrobić na " "stronach archiwalnych (blog, lista produktów itp.) W tym przypadku musisz " "użyć ustawień globalnych." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "Ustawienia obrazu tła dostępne są w trybie podglądu na żywo tylko dla wersji " "Professional. Możesz je wypróbować w trybie podglądu ,,aktualny styl”, ale " "nie zostaną one zapisane." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Przezroczystość obrazu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Pozycja obrazu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "Rozmiar obrazu" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Dziedzicz" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Pola wejściowe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Nazwa szablonu stylu wejściowego" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Wklejające" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Kursywa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Kolor etykiet" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Rozmiar czcionki etykiet" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Ustawienia etykiet" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Licencja" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "Wysokość linii" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Kolor odsyłaczy" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Kolor odsyłaczy po najechaniu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Ustawienia odsyłaczy" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "Na żywo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Załaduj style w znaczniku <body>" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "Załaduj style wewnątrz tagu <body> na tej stronie" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Ustaw po jednym polu wielokrotnego wyboru w wierszu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "Ustawić pełną szerokość?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "Ustawić pola wejściowe na pełną szerokość?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Ustaw po jednym polu jednokrotnego wyboru w wierszu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Margines" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Widok w komórce" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "NIE" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "Brak pozycji formularza Contact Form 7 do podglądu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "Żaden formularz nie jest zmodyfikowany przy pomocy szablonu stylu. Aby " "zastosować aktualny szablon do tego formularza kliknij ,,Aktywuj styl do " "aktualnego formularza”." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "Nie wybrano żadnej wtyczki do zainstalowania" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "Nie wybrano żadnego szablonu stylu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "Normalny" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Kursywa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "Ustawienie jednego pola wielokrotnego i jednokrotnego wyboru w wierszu jest " "możliwe tylko w wersji Professional. Możesz je wypróbować w trybie podglądu ," ",aktualny styl”, ale nie zostaną one zapisane." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Przezroczystość" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Otwórz Styler" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "Zaloguj się, aby zobaczyć konto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "Oryginalny rozmiar" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Kontur" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "Uwypuklające" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Wypełnienie" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Wprowadź szablonu stylu wejściowego" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Podgląd" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "Tryb podglądu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Podgląd niezmodyfikowanego stylu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Promień" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "Powtórz obydwa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "Powtórz poziomo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "Powtórz pionowo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Przywróć ustawienia domyślne" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Wypukłe" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "Zapisz" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "Zapisano" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Widok drugiej kolumny" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Wybierz szablon stylu dla tego formularza" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Ustawienia" #: admin/class-cf7-customizer-admin-ajax.php:210 #| msgid "Settings saved as" msgid "Settings saved as " msgstr "Ustawienia zapisane jako" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "Cień" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "Kolor cienia" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Pozycja cienia" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Jednolite ciągłe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 #| msgid "" #| "Some themes or page builders (e.g. Thrive Architect, OptimizePress etc.) " #| "could remove inline styles inside tag. Loading style scheme within " #| " tag will fix this issue and show your forms styled." msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Niektóre motywy lub kreatory stron (np. Thrive Architect, OptimizePress itp.)" " mogą usuwać style lokalne (inline styles) wewnątrz tagu . Dodanie " "szablonu stylu wewnątrz tagu naprawi ten problem i twoje formularze " "będą miały zmodyfikowany styl." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "Tryb podzielonego widoku" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "Widok podzielony" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Promień rozproszenia" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Zacznij modyfikować styl" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Zacznij od utworzenia Domyślnego szablonu stylu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Zacznij od stworzenia swojego " "pierwszego Contact Form 7" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "Rozpocznij 14-dniowy bezpłatny okres próbny ze wszystkimi funkcjami " "Professional tutaj!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Krok 1. Kliknij na link ,,Edit with Thrive Architect\" (Edytuj z Thrive " "Architect) na liście stron administratora" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "Krok 2. Kliknij ikonę ,,Settings” (Ustawienia) w prawym pionowym menu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 #| msgid "Step 3. \"Advanced settings\" => \"CSS in the section\"" msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" "Krok 3. ,,Advanced settings” (Ustawienia zaawansowane) => ,,CSS in the " " section” (CSS w sekcji )" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 #| msgid "Step 4. Make sure that \"Do not strip CSS from \" is checked" msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "Krok 4. Upewnij się, że opcja ,,Do not strip CSS from ” (Nie usuwaj " "CSS z sekcji) jest zaznaczona" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Krok 5. Kliknij przycisk ,,SAVE WORK” (ZAPISZ PRACĘ)" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "Modyfikuj globalnie jednym kliknięciem styl wszystkich formularzy" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "" "Modyfikuj indywidualnie styl każdego formularza przy pomocy dowolnego " "szablonu" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Szablon stylu jest wyłączony dla wszystkich formularzy" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Szablon stylu jest wyłączony dla tego formularza" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Szablon stylu jest włączony dla tego formularza" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "Szablon stylu nie jest wybrany" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "Lista szablonów stylów" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Podgląd szablonów stylów" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "Ustawienia szablonów stylów" #: admin/class-cf7-customizer-admin-ajax.php:298 #| msgid "Style will be loaded in tag" msgid "Style will be loaded in tag" msgstr "Styl zostanie dodany do tagu" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "Styl zostanie załadowany w tagu " #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Udało się" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "Wsparcie i KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Widok w tablecie" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Kolor tekstu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #| msgid "" #| "This form is styled with %s globally, you can style it with current Style " #| "scheme." msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Styl tego formularza modyfikowany jest globalnie przy pomocy %s, możesz go " "modyfikować przy pomocy aktualnego Szablonu stylu." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #| msgid "" #| "This form is styled with %s, you can style it with current Style scheme." msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Styl tego formularza modyfikowany jest przy pomocy %s, możesz go modyfikować " "przy pomocy aktualnego Szablonu stylu." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Styl tego formularza modyfikowany jest przy pomocy aktualnego Szablonu stylu," " możesz go wyłączyć i użyć ustawień globalnych." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Styl tego formularza nie jest modyfikowany przy pomocy żadnego Szablonu " "stylu, możesz włączyć aktualny Szablon dla tego formularza albo ustawić " "globalny Szablon stylu klikając na ,,Użyj do wszystkich formularzy” poniżej " "nazwy szablonu." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Ten szablon nie istnieje" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Ten Szablon stylu jest włączony globalnie dla wszystkich formularzy. Jeżeli " "do formularza Contact Form 7 chcesz użyć stylu z twojego motywu kliknij " "przycisk ,,Wyłącz”." #. Author of the plugin msgid "Tobias Conrad" msgstr "Tobias Conrad" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "Tutorial" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Niezmodyfikowany styl" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "Uaktualnij do wersji Pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Załaduj obraz" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "Długość pionowa" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Witaj" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "Szerokość" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "WOW Style Contact Form 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "TAK" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 #| msgid "" #| "You can create new style scheme with current scheme settings. If you want to " #| "create blank style scheme uncheck checkbox below." msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "Możesz utworzyć nowy szablon stylu z aktualnymi ustawieniami szablonu. Jeśli " "chcesz utworzyć pusty szablon stylu odznacz pole wyboru poniżej." #: admin/class-cf7-customizer-admin-ajax.php:237 #| msgid "You cannot delete" msgid "You can not delete" msgstr "Nie można usunąć" #: public/class-cf7-customizer-public.php:471 #| msgid "You cannot use this settings on this page type" msgid "You can not use this settings on this page type" msgstr "Nie można używać tych ustawień na stronach tego typu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "Nie masz żadnych pozycji formularza Contact Form 7" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "Nie używasz żadnych Szablonów stylów do formularza Contact Form 7. Kliknij ,," "Użyj do wszystkich formularzy”, aby używać aktualnego Szablonu globalnie." languages/cf7-styler-pl_PL.mo000064400000047074147600046700012072 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'(* ***B*&*&+](+ ++ + +k+*, <,H,Z,o,,, ,,,Y- y--)-- -------).0.8.W. s.W./00 11 !1 -1,;1'h1&112 2 2%2$3+3D3U3 f3 t333/333 4 4(4;4T4h44556=899:: ::!: ;; !;/;H;[;d;u;;;;4;F;3@<t<0<3<<==3=I=-=##>G>P>X>(?:?!I?k?~? ? ?%??!??@ @@*@;@[@d@k@t@)@ @@@ @@@ AB.B?BUB0oB^BBqDHDpDqDE7EBEO1F8F2F1FG>GWGsGG'G G GG HHHu?II9J0K IKWK`KvKKKKK KKKsL8L3LL M$MM`M- disable style scheme -- select -14-days Trial%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Loco https://localise.biz/ Project-Id-Version: Wow style (ES) Language: pl-PL Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); Report-Msgid-Bugs-To: POT-Creation-Date: 2020-04-18 12:29+0000 PO-Revision-Date: 2023-04-15 16:04+0000 Last-Translator: Tobias support@saleswonder.biz Language-Team: Polish X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- wyłącz szablon stylu -- wybierz -14-dniowy okres próbny%s używany dla wszystkich formularzy globalnie, kliknij "Użyj dla wszystkich formularzy", jeśli chcesz użyć bieżącego schematu.Aktywuj styl do wszystkich formularzy*Aktywuj styl dla aktualnego formularzaOpcjonalnie możesz wyłączyć czyszczenie stylów podobnie jak we wtyczce Thrive Architect:Kolor tłaPrzezroczystość tłaKolor tłaObraz tłaPrzed użyciem wtyczki WOW Style Contact Form 7, musisz zainstalować i aktywować wtyczkę Contact Form 7.Promień rozmyciaObramowanieKolor obramowaniaPromień obramowaniaRodzaj obramowaniaSzerokość obramowaniaObramowanie przyciskuPrzyciskiAutor: Takayuki MiyoshiWtyczka do modyfikacji stylów formularza Contact Form 7 wymaga zainstalowania i aktywacji wtyczki Contact Form 7. Możesz pobrać %s.Formularz CF7 nie jest wybrany.CF7 StylerAnulujPola wielokrotnego i jednokrotnego wyboruWyczyść styleWyczyśćZamknijKolorLista formularzy CF7ZmieśćSkopiuj ustawienia bieżącego szablonu styluPokryjUtwórzUtwórz wiele szablonów styluUtwórz nowy szablon stylu!Aktualny stylObecnie %s formularz jest stylizowany za pomocą %s. Tak jak w wersji darmowej możesz stylizować tylko jeden formularz w tym samym czasie i jeśli aktywujesz styl dla bieżącego formularza, styl zostanie usunięty z innych formularzy.Aktualnie styl formularza %s jest skonfigurowany przy pomocy %s. Ponieważ w wersji darmowej w danym momencie możesz modyfikować styl tylko jednego formularza, jeśli aktywujesz styl dla aktualnego formularza, styl zostanie usunięty z innego formularza.Niestandardowy CSSNiestandardowy kod CSSKreskowaneSzablon domyślnyUsuń obrazWidok pulpituWyłączenie stylu dla wszystkich formularzyWyłącz styl dla aktualnego formularzaCzy chcesz otrzymać wersję premium stylera za darmo? Następnie Wprowadź swoją licencję WP2LEADS pro lub zdobądź licencję tutaj!Chcesz zapisać zmiany przed opuszczeniem strony?KropkowanePodwójneDuplikuj formularz w drugiej kolumnieBłądWyjdź z pełnego ekranuRodzina czcionekRozmiar czcionkiStyl czcionkiWaga czcionkiObraz i kolory tła formularzaDostosowanie formularzaWypełnienie, margines i obramowanie formularzaTekst formularzaNie wybrano formularzaPełny ekranWklęsłeDługość poziomaKolor tła po najechaniuKolor po najechaniuKolor tekstu po najechaniuJak z formularza kontaktowego Contact Form 7 zrobić formularz kontaktowy, który nie tylko będzie miał przyciągający uwagę profesjonalny wygląd oraz będzie łatwy w użyciu, ale także będzie miał wysoki współczynnik konwersji i będzie generował zapytania o oferty.Podoba mi się! ZapiszJeśli używasz page builderów, takich jak Thrive Architect, OptimizePress itp., sprawdź naszą bazę wiedzy, aby naprawić ewentualne błędyJeśli potrzebujesz załadować schemat stylu wewnątrz <body> tagu tylko na niektórych stronach, możesz to zrobić na frontend za pomocą przycisku "CF7 Styler". Ta funkcja jest dostępna tylko dla pojedynczych typów postów (stron, postów, produktów itp.) i w wersji premium. Nie możesz tego zrobić na stronach archiwalnych (blog, lista produktów itp.) W tym przypadku musisz użyć ustawień globalnych.Jeśli potrzebujesz załadować schemat stylu wewnątrz <body> tagu tylko na niektórych stronach, możesz to zrobić na frontend za pomocą przycisku "CF7 Styler". Ta funkcja jest dostępna tylko dla pojedynczych typów postów (stron, postów, produktów itp.). Nie możesz tego zrobić na stronach archiwalnych (blog, lista produktów itp.) W tym przypadku musisz użyć ustawień globalnych.Przezroczystość obrazuPozycja obrazuRozmiar obrazuUstawienia obrazu tła dostępne są w trybie podglądu na żywo tylko dla wersji Professional. Możesz je wypróbować w trybie podglądu ,,aktualny styl”, ale nie zostaną one zapisane.DziedziczPola wejścioweNazwa szablonu stylu wejściowegoWklejająceKursywaKolor etykietRozmiar czcionki etykietUstawienia etykietLicencjaWysokość liniiKolor odsyłaczyKolor odsyłaczy po najechaniuUstawienia odsyłaczyNa żywoZaładuj style w znaczniku <body>Załaduj style wewnątrz tagu <body> na tej stronieUstaw po jednym polu wielokrotnego wyboru w wierszuUstawić pełną szerokość?Ustawić pola wejściowe na pełną szerokość?Ustaw po jednym polu jednokrotnego wyboru w wierszuMarginesWidok w komórceNIEBrak pozycji formularza Contact Form 7 do podgląduŻaden formularz nie jest zmodyfikowany przy pomocy szablonu stylu. Aby zastosować aktualny szablon do tego formularza kliknij ,,Aktywuj styl do aktualnego formularza”.Nie wybrano żadnej wtyczki do zainstalowaniaNie wybrano żadnego szablonu styluNormalnyKursywaUstawienie jednego pola wielokrotnego i jednokrotnego wyboru w wierszu jest możliwe tylko w wersji Professional. Możesz je wypróbować w trybie podglądu ,,aktualny styl”, ale nie zostaną one zapisane.PrzezroczystośćOtwórz StylerZaloguj się, aby zobaczyć kontoOryginalny rozmiarKonturUwypuklająceWypełnienieWprowadź szablonu stylu wejściowegoPodglądPodgląd niezmodyfikowanego styluTryb podgląduPromieńPowtórz obydwaPowtórz poziomoPowtórz pionowoPrzywróć ustawienia domyślneWypukłeZapiszZapisanoWidok drugiej kolumnyWybierz szablon stylu dla tego formularzaUstawieniaUstawienia zapisane jakoCieńKolor cieniaPozycja cieniaJednolite ciągłeNiektóre motywy lub kreatory stron (np. Thrive Architect, OptimizePress itp.) mogą usuwać style lokalne (inline styles) wewnątrz tagu . Dodanie szablonu stylu wewnątrz tagu naprawi ten problem i twoje formularze będą miały zmodyfikowany styl.Tryb podzielonego widokuWidok podzielonyPromień rozproszeniaZacznij modyfikować stylZacznij od utworzenia Domyślnego szablonu styluZacznij od stworzenia swojego pierwszego Contact Form 7Rozpocznij 14-dniowy bezpłatny okres próbny ze wszystkimi funkcjami Professional tutaj!Krok 1. Kliknij na link ,,Edit with Thrive Architect" (Edytuj z Thrive Architect) na liście stron administratoraKrok 2. Kliknij ikonę ,,Settings” (Ustawienia) w prawym pionowym menuKrok 3. ,,Advanced settings” (Ustawienia zaawansowane) => ,,CSS in the section” (CSS w sekcji )Krok 4. Upewnij się, że opcja ,,Do not strip CSS from ” (Nie usuwaj CSS z sekcji) jest zaznaczonaKrok 5. Kliknij przycisk ,,SAVE WORK” (ZAPISZ PRACĘ)Modyfikuj globalnie jednym kliknięciem styl wszystkich formularzyModyfikuj indywidualnie styl każdego formularza przy pomocy dowolnego szablonuSzablon stylu jest wyłączony dla wszystkich formularzySzablon stylu jest wyłączony dla tego formularzaSzablon stylu jest włączony dla tego formularzaSzablon stylu nie jest wybranyLista szablonów stylówPodgląd szablonów stylówUstawienia szablonów stylówStyl zostanie dodany do taguStyl zostanie załadowany w tagu Udało sięWsparcie i KBWidok w tablecieKolor tekstuTen Szablon stylu jest włączony globalnie dla wszystkich formularzy. Jeżeli do formularza Contact Form 7 chcesz użyć stylu z twojego motywu kliknij przycisk ,,Wyłącz”.Styl tego formularza modyfikowany jest globalnie przy pomocy %s, możesz go modyfikować przy pomocy aktualnego Szablonu stylu.Styl tego formularza modyfikowany jest przy pomocy %s, możesz go modyfikować przy pomocy aktualnego Szablonu stylu.Styl tego formularza modyfikowany jest przy pomocy aktualnego Szablonu stylu, możesz go wyłączyć i użyć ustawień globalnych.Styl tego formularza nie jest modyfikowany przy pomocy żadnego Szablonu stylu, możesz włączyć aktualny Szablon dla tego formularza albo ustawić globalny Szablon stylu klikając na ,,Użyj do wszystkich formularzy” poniżej nazwy szablonu.Ten szablon nie istniejeTobias ConradTutorialNiezmodyfikowany stylUaktualnij do wersji ProZaładuj obrazDługość pionowaWOW Style Contact Form 7WitajSzerokośćTAKMożesz utworzyć nowy szablon stylu z aktualnymi ustawieniami szablonu. Jeśli chcesz utworzyć pusty szablon stylu odznacz pole wyboru poniżej.Nie można usunąćNie można używać tych ustawień na stronach tego typuNie masz żadnych pozycji formularza Contact Form 7Nie używasz żadnych Szablonów stylów do formularza Contact Form 7. Kliknij ,,Użyj do wszystkich formularzy”, aby używać aktualnego Szablonu globalnie.usuniętywłączony dla wszystkich formularzyhttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/languages/cf7-styler-ja.po000064400000111572147600046700011454 0ustar00msgid "" msgstr "" "Language: ja\n" "POT-Creation-Date: 2020-02-06 11:35+0000\n" "Plural-Forms: nplurals=1; plural=0;\n" "PO-Revision-Date: 2023-04-15 16:09+0000\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: WOW Style Contact Form 7\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: Japanese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Loco-Version: 2.3.3; wp-5.4" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "-を無効にすスタイルのスキーム-" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "-選択-" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14日間の試用" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "い%s\" " "使用のためのすべての形の世界をクリックし\"利用のためのすべてのフォーム\"を利用する場合は現在のスキームです。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "起動スタイルのためのすべての形態*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "起動スタイルを現在の形" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "の代替として無数の組み合わせで自分だけのクリーンのスタイルのように生建築家:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "背景色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "背景画像" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "使用前になんとスタイルのContact Form7のインストールする必要があり、活性化のContact Form7の引き出しおよび設定ができます" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "背景の色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "BG不透明度" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "ブ半径" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "ボーダー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "ボーダーの色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "ボーダーの半径" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "ボーダータイプ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "ボーダー幅" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "ボタンの国境" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "ボタン" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "による三好孝之" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "消" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "CF7\"カスタマイズが必要でContact Form7のプラグインを設置する必要がある。 ダウンロードでき%s\"." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "CF7形が選択されていない" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7Styler" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "チェックボックス&Radiobuttons" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "クリーンスタイル" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "明らか" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "近" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Contact form7のリスト" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "を含む" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "コピースタイルのスキームに設定" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "カバー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "の作成" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "複数作成するスタイルスキーム" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "新しいスタイルのスキーム!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "現在のスタイル" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "現在 %s 形式との %s\". " "として無料版できるスタイルの一形態であれば起動スタイルのままの形では、スタイルから削除されるか。" #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "現在 %s 形式との %s\". " "として無料版できるスタイルの一形態であれば起動スタイルのままの形では、スタイルから削除されるか。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "カスタムCSS" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "カスタムCSSコード" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "疾走" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "デフォルトのスキーム" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "画像の削除" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "削除" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "デスクトップビュー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "無効化スタイルのためのすべての形態" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "無効化スタイルを現在の形" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "スタイラープレミアムバージョンを無料で入手したいですか? 次に、 WP2LEADS proライセンスを入力するか、ライセンスを取得しますこちら!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "い変更を保存前のページに表示すべき事は何か?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "点在" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "ダブル" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "重複した形態の第二の柱" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "有効なすべての形態" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "エラー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "口全画面" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "フォントファミリ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "文字サイズ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "フォントスタイル" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "フォント重量" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "形態画像と色" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "形式のカスタマイズ" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "形が選択されていない" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "形のパディング、マージン&ボーダー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "テキスト形式" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "全画面" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "溝" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "横の長さ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "ホバー背景の色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "ホバー色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "ホテキストカラーを設定します。" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "どのようにご連絡をおform7形式に変換する使いやすく、プロアは、お問い合わせフォーム\"調査\"の発生または目を引く形" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "https://saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "嬉しいです。 保存" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Thrive Architect、OptimizePressなどのページビルダーを使用している場合は、ナレッジベースページ" "を確認してください 考えられる問題の修正" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "が必要な場合は負荷のスタイルのスキーム内の <body> " "タグのみ一部のページできなフロントエンドを使用\"CF7Styler\"ボタンを押します。 この機能はシングルポスト型(ページ、製品等) " "やプレミアムバージョン。 できませんアーカイブのページのブログでは、製品リストなど)をお使いになる場合はグローバルを設定します。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "が必要な場合は負荷のスタイルのスキーム内の <body> " "タグのみ一部のページできなフロントエンドを使用\"CF7Styler\"ボタンを押します。 この機能はシングルポスト型(ページ、製品ます。 " "できませんアーカイブのページのブログでは、製品リストなど)をお使いになる場合はグローバルを設定します。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "背景画像の設定を有ライブモードのみのための専門のバージョン。 できる試験でのプレビューモード\"現在のスタイル\"で保存されません。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "画像の不透明度" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "画像の位置" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "画像サイズ" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "継承" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "入力分野" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "入力スタイルのスキームタイトル" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "挿入図" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Italic" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "ラベルの色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "ラベルフォントサイズ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "ラベル設定" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "ライセンス" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "ライン高さ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "リンクの色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "リンクホバー色" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "リンクの設定" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "ライブ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "負荷スタイルでの <body> タグ" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "負荷スタイル内に <body> タグこのページ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "をチェックボックス項目の配線" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "全幅?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "入力分野全幅?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "うラジオボタンを商品一行に一つの" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "証拠金" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "モバイル端末上の表示" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "NO" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "ない形で7項目プレビュー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "無形式のスタイルのスキーム\"をクリックし起動スタイルを現在のフォーム\"ボタンの適用を現行スキームするようになります。" #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "ないプラグインの選択イ" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "なスタイルのスキームを選択" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "通常の" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "斜め" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "一行に一つのスタイルのチェックボックスとradiobuttonsライブモードのみのための専門のバージョン。 " "できる試験でのプレビューモード\"現在のスタイル\"で保存されません。" #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "不透明度" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "開放感" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "オプトインでアカウントを見る" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "オリジナルサイズ" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "概要" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "当初" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "パディング" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "ご入力くださいスタイルのスキームタイトル" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "プレビュー" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "プレビュー表示モード" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "プレビュー Unstyled" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "半径" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "繰り返し両" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "繰り返し水平" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "繰り返し垂直" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "デフォルトにリセット" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "尾根" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "保存" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "保存" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "第二列ビュー" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "選択スタイルのスキームのためのこ" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "設定" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "設定として保存 " #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "影" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "影の色" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "影の位置" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "固体" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "一部のテーマやページビルダー(feである。 繁栄建築家OptimizePress等) を除去することが可能であることインラインのスタイル内の " "<head> タグです。 荷積みスタイルのスキーム内の <body> " "タグの組み合わせが決定されるこの問題提示の形式." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "分割モード" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "分割ビュー" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "広がる半径" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "スタイリング開始" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "最初に作成デフォルトのスタイルのスキーム" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "タを作成する 最初のContact Form7" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "始まりは14日間無料お試しすべてのプロの機能 がここに!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "ステップ1です。 編集\"をクリックしと繁栄建築家\"のリンクをクリック管理者ページリスト" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "ステップ2に進みます。 をクリックし\"設定\"アイコンを右に縦メニュー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "Step3. \"詳細設定\"=>\"CSSの<head>セクション\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "Step4. になっていることを確認しないストリップCSSからの<head>\"にチェック" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "ステップ5です。 \"保存\"ボタンをクリックします仕事\"ボタン" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "スタイル全てにワンクリックで世界的に" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "スタイルそれぞれの形態とスタイルのスキームを個別に" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "スタイルのスキームを無効にすべての形態" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "スタイルのスキームを無効にすることを前提" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "スタイルのスキームを有効にすることを前提" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "スタイルのスキームが選択されていない" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "スタイルスキーム一覧" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "スタイルスキームのプレビュー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "スタイルスキームに設定" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "スタイルが読み込まれ タグ" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "スタイルが読み込まれ タグ" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "成功" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "サポート&KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "タブレットビュー" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "テキストカラーを設定します。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "この形式との %s\" 世界できるスタイルで現在のスタイルスキームです。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "この形式との %s\"きスタイルで現在のスタイルスキームです。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "この形式の現在のスタイルのスキームにオフにすることもできますし、グローバルを設定します。" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "この形式は表のとスタイルのスキームを得ることができ現在のスキームはこの形式からインターネットが世界中で画一的なスタイルのスキームをクリック\"使用のためのすべてのフォームは下記スキームのタイトル。" #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "このスキームが存在していない" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "このスタイルのスキームを有効に世界のすべての形態がある。 " "使用する場合にプレゼンテーションのスタイルを問合せフォーム7\"をクリックを無効にす\"ボタンを押します。" #. Author of the plugin msgid "Tobias Conrad" msgstr "Tobiasコンラッド" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "チュートリアル" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Unstyled" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "プPro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "画像アップロード" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "縦方向の長さ" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "歓迎" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "幅" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "なんとスタイルのContact Form7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "あり" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "を作成できます新しいスタイルのスキームの現在のスキームを設定します。 を処理しなければいけない空白のスタイルschem unchekチェックボックスです。" #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "だけを削除することはできません" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "使用できませんこの設定はこのページタイプ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "一切していないのContact Form7項目" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "使いでないスタイルのスキームのための問合せフォーム7. クリック\"使用のためのすべてのフォーム\"現在のスキームです。" languages/cf7-styler-ja.mo000064400000047527147600046700011461 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'(,))))1*!*p* c+p+ + ++ ;, E,R,e,{,,, ,,,!T- v--)-- ---- --- +. 5.*?.%j.../00000031$;1Z`1B22 3!3 13 ;3H3a3q33335344 44>4 B4O4 e4-r44A5[5Y689999: :-: :;;;6;F;V;f;v;; ;9;H;*,<W<_<0s< <<<"<<!='= === > >*>#?%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Language: ja POT-Creation-Date: 2020-02-06 11:35+0000 Plural-Forms: nplurals=1; plural=0; PO-Revision-Date: 2023-04-15 16:09+0000 X-Generator: Loco https://localise.biz/ Project-Id-Version: WOW Style Contact Form 7 Report-Msgid-Bugs-To: Last-Translator: Language-Team: Japanese MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Loco-Version: 2.3.3; wp-5.4-を無効にすスタイルのスキーム--選択-14日間の試用い%s" 使用のためのすべての形の世界をクリックし"利用のためのすべてのフォーム"を利用する場合は現在のスキームです。起動スタイルのためのすべての形態*起動スタイルを現在の形の代替として無数の組み合わせで自分だけのクリーンのスタイルのように生建築家:背景の色BG不透明度背景色背景画像使用前になんとスタイルのContact Form7のインストールする必要があり、活性化のContact Form7の引き出しおよび設定ができますブ半径ボーダーボーダーの色ボーダーの半径ボーダータイプボーダー幅ボタンの国境ボタンによる三好孝之CF7"カスタマイズが必要でContact Form7のプラグインを設置する必要がある。 ダウンロードでき%s".CF7形が選択されていないCF7Styler消チェックボックス&Radiobuttonsクリーンスタイル明らか近色Contact form7のリストを含むコピースタイルのスキームに設定カバーの作成複数作成するスタイルスキーム新しいスタイルのスキーム!現在のスタイル現在 %s 形式との %s". として無料版できるスタイルの一形態であれば起動スタイルのままの形では、スタイルから削除されるか。現在 %s 形式との %s". として無料版できるスタイルの一形態であれば起動スタイルのままの形では、スタイルから削除されるか。カスタムCSSカスタムCSSコード疾走デフォルトのスキーム画像の削除デスクトップビュー無効化スタイルのためのすべての形態無効化スタイルを現在の形スタイラープレミアムバージョンを無料で入手したいですか? 次に、 WP2LEADS proライセンスを入力するか、ライセンスを取得しますこちら!い変更を保存前のページに表示すべき事は何か?点在ダブル重複した形態の第二の柱エラー口全画面フォントファミリ文字サイズフォントスタイルフォント重量形態画像と色形式のカスタマイズ形のパディング、マージン&ボーダーテキスト形式形が選択されていない全画面溝横の長さホバー背景の色ホバー色ホテキストカラーを設定します。どのようにご連絡をおform7形式に変換する使いやすく、プロアは、お問い合わせフォーム"調査"の発生または目を引く形嬉しいです。 保存Thrive Architect、OptimizePressなどのページビルダーを使用している場合は、ナレッジベースページを確認してください 考えられる問題の修正が必要な場合は負荷のスタイルのスキーム内の <body> タグのみ一部のページできなフロントエンドを使用"CF7Styler"ボタンを押します。 この機能はシングルポスト型(ページ、製品等) やプレミアムバージョン。 できませんアーカイブのページのブログでは、製品リストなど)をお使いになる場合はグローバルを設定します。が必要な場合は負荷のスタイルのスキーム内の <body> タグのみ一部のページできなフロントエンドを使用"CF7Styler"ボタンを押します。 この機能はシングルポスト型(ページ、製品ます。 できませんアーカイブのページのブログでは、製品リストなど)をお使いになる場合はグローバルを設定します。画像の不透明度画像の位置画像サイズ背景画像の設定を有ライブモードのみのための専門のバージョン。 できる試験でのプレビューモード"現在のスタイル"で保存されません。継承入力分野入力スタイルのスキームタイトル挿入図Italicラベルの色ラベルフォントサイズラベル設定ライセンスライン高さリンクの色リンクホバー色リンクの設定ライブ負荷スタイルでの <body> タグ負荷スタイル内に <body> タグこのページをチェックボックス項目の配線全幅?入力分野全幅?うラジオボタンを商品一行に一つの証拠金モバイル端末上の表示NOない形で7項目プレビュー無形式のスタイルのスキーム"をクリックし起動スタイルを現在のフォーム"ボタンの適用を現行スキームするようになります。ないプラグインの選択イなスタイルのスキームを選択通常の斜め一行に一つのスタイルのチェックボックスとradiobuttonsライブモードのみのための専門のバージョン。 できる試験でのプレビューモード"現在のスタイル"で保存されません。不透明度開放感オプトインでアカウントを見るオリジナルサイズ概要当初パディングご入力くださいスタイルのスキームタイトルプレビュープレビュー Unstyledプレビュー表示モード半径繰り返し両繰り返し水平繰り返し垂直デフォルトにリセット尾根保存保存第二列ビュー選択スタイルのスキームのためのこ設定設定として保存 影影の色影の位置固体一部のテーマやページビルダー(feである。 繁栄建築家OptimizePress等) を除去することが可能であることインラインのスタイル内の <head> タグです。 荷積みスタイルのスキーム内の <body> タグの組み合わせが決定されるこの問題提示の形式.分割モード分割ビュー広がる半径スタイリング開始最初に作成デフォルトのスタイルのスキームタを作成する 最初のContact Form7始まりは14日間無料お試しすべてのプロの機能 がここに!ステップ1です。 編集"をクリックしと繁栄建築家"のリンクをクリック管理者ページリストステップ2に進みます。 をクリックし"設定"アイコンを右に縦メニューStep3. "詳細設定"=>"CSSの<head>セクション"Step4. になっていることを確認しないストリップCSSからの<head>"にチェックステップ5です。 "保存"ボタンをクリックします仕事"ボタンスタイル全てにワンクリックで世界的にスタイルそれぞれの形態とスタイルのスキームを個別にスタイルのスキームを無効にすべての形態スタイルのスキームを無効にすることを前提スタイルのスキームを有効にすることを前提スタイルのスキームが選択されていないスタイルスキーム一覧スタイルスキームのプレビュースタイルスキームに設定スタイルが読み込まれ タグスタイルが読み込まれ タグ成功サポート&KBタブレットビューテキストカラーを設定します。このスタイルのスキームを有効に世界のすべての形態がある。 使用する場合にプレゼンテーションのスタイルを問合せフォーム7"をクリックを無効にす"ボタンを押します。この形式との %s" 世界できるスタイルで現在のスタイルスキームです。この形式との %s"きスタイルで現在のスタイルスキームです。この形式の現在のスタイルのスキームにオフにすることもできますし、グローバルを設定します。この形式は表のとスタイルのスキームを得ることができ現在のスキームはこの形式からインターネットが世界中で画一的なスタイルのスキームをクリック"使用のためのすべてのフォームは下記スキームのタイトル。このスキームが存在していないTobiasコンラッドチュートリアルUnstyledプPro画像アップロード縦方向の長さなんとスタイルのContact Form7歓迎幅ありを作成できます新しいスタイルのスキームの現在のスキームを設定します。 を処理しなければいけない空白のスタイルschem unchekチェックボックスです。だけを削除することはできません使用できませんこの設定はこのページタイプ一切していないのContact Form7項目使いでないスタイルのスキームのための問合せフォーム7. クリック"使用のためのすべてのフォーム"現在のスキームです。削除有効なすべての形態https://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/languages/cf7-styler-it_IT.po000064400000111444147600046700012070 0ustar00msgid "" msgstr "" "Language: it-IT\n" "POT-Creation-Date: 2023-04-15 15:26+0000\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "PO-Revision-Date: 2023-04-15 16:10+0000\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: WOW Style Contact Form 7\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: Italian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- disabilitare lo schema di stile -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "- selezionare -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14 giorni di prova" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s utilizzato per tutti i moduli a livello globale, fare " "clic su \"Usa per tutti i moduli\" se si desidera utilizzare lo schema " "corrente." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Attivare lo stile per tutti i moduli*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Attivare lo stile per il modulo corrente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "In alternativa, è possibile disattivare la pulizia degli stili come in " "Thrive Architect:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Colore di sfondo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Immagine di sfondo" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Prima di utilizzare WOW Style Contact Form 7, è necessario installare e " "attivare il plugin Contact Form 7." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "Colore BG" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "Opacità BG" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Raggio di sfocatura" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "Confine" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Colore del bordo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Raggio del bordo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Tipo di confine" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "Larghezza del bordo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "Bordo del pulsante" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Pulsanti" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Di Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "Annullamento" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "CF7 Customizer richiede che il plugin Contact Form 7 sia installato e attivo." " È possibile scaricare %s." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "CF7 Il modulo non è selezionato" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7 Styler" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Caselle di controllo e radiobottoni" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "Stili puliti" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "Libero" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "Chiudere" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Colore" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Modulo di contatto 7 elenco" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "Contenere" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Copiare le impostazioni dello schema di stile corrente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "Copertina" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "Creare" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Creare più schemi di stile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Create un nuovo schema di stile!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Stile attuale" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "Attualmente il modulo %s " "è stilizzato con %s" ". Come nella versione gratuita, è possibile stilizzare solo un modulo alla " "volta e se si attiva lo stile per il modulo corrente, lo stile verrà rimosso " "dagli altri moduli." #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "Attualmente il modulo %s è stilizzato con " "%s. Come nella versione gratuita, è possibile stilizzare solo un " "modulo alla volta e se si attiva lo stile per il modulo corrente, lo stile " "verrà rimosso dagli altri moduli." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "CSS personalizzato" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "Codice CSS personalizzato" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Tratteggiato" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Schema predefinito" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "Cancellare l'immagine" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "cancellato" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Vista sul desktop" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Disabilita lo stile per tutti i moduli" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Disabilita lo stile per il modulo corrente" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "Vuoi ottenere la versione premium di Styler gratuitamente? Allora inserisci la tua licenza " "WP2LEADS pro o ottieni una licenza qui!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "Volete salvare le modifiche prima di lasciare la pagina?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "A puntini" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Doppio" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Modulo duplicato nella seconda colonna" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "abilitato per tutti i moduli" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Errore" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Uscita dallo schermo intero" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Famiglia di caratteri" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "Dimensione del carattere" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Stile del carattere" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "Peso del carattere" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Immagine BG del modulo e colori" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Personalizzazione del modulo" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Il modulo non è selezionato" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Imbottitura, margine e bordo del modulo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Testo del modulo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "Schermo intero" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "Scanalatura" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Lunghezza orizzontale" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Colore del bordo del mouse" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "Colore Hover" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Colore del testo al passaggio del mouse" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Come trasformare il vostro modulo contact form7 in un modulo di contatto che " "converte e che è facile da usare e in stile professionale, in un generatore " "di lead \"survey\" o in un modulo che cattura l'attenzione" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "https://saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "Sono felice di questo! Risparmio" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Se si utilizzano page builder come Thrive Architect, OptimizePress ecc., " "consultare la nostra pagina Base di conoscenza per risolvere eventuali " "problemi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Se avete bisogno di caricare lo schema di stile all'interno del tag " "<body> solo su alcune pagine, potete farlo nel frontend " "utilizzando il pulsante \"CF7 Styler\". Questa funzione è disponibile solo " "per i tipi di post singoli (pagine, post, prodotti ecc.) e nella versione " "premium. Non è possibile farlo sulle pagine degli archivi (blog, elenco di " "prodotti, ecc.), in questo caso è necessario utilizzare le impostazioni " "globali." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Se è necessario caricare lo schema di stile all'interno del tag <" "body> solo su alcune pagine, è possibile farlo nel frontend " "utilizzando il pulsante \"CF7 Styler\". Questa funzione è disponibile solo " "per i singoli tipi di post (pagine, post, prodotti ecc.). Non è possibile " "farlo sulle pagine degli archivi (blog, elenco di prodotti, ecc.), in questo " "caso è necessario utilizzare le impostazioni globali." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "Le impostazioni dello sfondo dell'immagine sono disponibili in modalità live " "solo per la versione Professional. È possibile provarle nella modalità di " "anteprima \"stile corrente\", ma non verranno salvate." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Opacità dell'immagine" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Posizione dell'immagine" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "Dimensione dell'immagine" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Ereditare" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Campi di input" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Titolo dello schema di stile di input" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Inserto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Corsivo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Etichette Colore" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Dimensione del carattere delle etichette" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Impostazioni delle etichette" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Licenza" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "Altezza della linea" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Link Colore" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Colore del passaggio dei link" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Impostazioni dei collegamenti" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "In diretta" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Caricamento degli stili nel tag <body>" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "" "Caricare gli stili all'interno del tag <body> in questa " "pagina" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Rendere la voce della casella di controllo una per riga" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "Fare il pieno di larghezza?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "Rendere i campi di input a larghezza piena?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Rendere l'elemento del radiobottone uno per riga" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Margine" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Vista mobile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "NO" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "Nessun elemento di Contact Form 7 per l'anteprima" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "Nessun modulo è stato creato con uno schema di stile, fare clic sul pulsante " "\"Attiva lo stile per il modulo corrente\" per applicare lo schema corrente " "a questo modulo." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "Non è stato selezionato alcun plugin da installare" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "Non è stato selezionato alcuno schema di stile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "Normale" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Obliquo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "Stili per riga per caselle di controllo e radiobottoni in modalità live solo " "per la versione Professional. È possibile provarlo in modalità anteprima " "\"stile corrente\", ma non verrà salvato." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Opacità" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Styler aperto" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "Opt-in per vedere l'account" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "Dimensione originale" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Schema" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "Esternalizzazione" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Imbottitura" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Inserire il titolo dello schema di stile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Anteprima" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "Modalità anteprima" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Anteprima Unstyled" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Raggio" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "Ripetere entrambi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "Ripetizione orizzontale" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "Ripetizione verticale" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Ripristino delle impostazioni predefinite" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Crinale" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "Risparmiare" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "Salvati" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Vista della seconda colonna" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Selezionare lo schema di stile per questo modulo" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Impostazioni" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "Impostazioni salvate come" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "Ombra" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "Colore dell'ombra" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Posizione d'ombra" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Solido" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Alcuni temi o page builder (ad esempio Thrive Architect, OptimizePress ecc.) " "potrebbero rimuovere gli stili in linea all'interno del tag <" "head>. Il caricamento dello schema di stile all'interno del tag " "<body> risolverà questo problema e mostrerà i moduli in " "stile." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "Modalità Split" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "Vista divisa" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Raggio di diffusione" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Iniziare lo styling" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Iniziare con la creazione dello schema di stile predefinito" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Iniziate creando il vostro primo " "Contact Form 7" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "Inizia la tua prova gratuita di 14 giorni con tutte le funzioni Professional " "qui!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Passo 1. Fare clic sul link \"Modifica con Thrive Architect\" nell'elenco " "della pagina di amministrazione" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "" "Passo 2. Fare clic sull'icona \"Impostazioni\" nel menu verticale di destra" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" "Passo 3. \"Impostazioni avanzate\" => \"CSS nella sezione <head>\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "Passo 4. Assicurarsi che sia selezionata l'opzione \"Non rimuovere i CSS da " "<head>\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Passo 5. Fate clic sul pulsante \"SALVA LAVORO" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "Modellare tutti i moduli con un solo clic a livello globale" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "Modellare ogni modulo con qualsiasi schema di stile individualmente" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Schema di stile disabilitato per tutti i moduli" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Schema di stile disabilitato per questo modulo" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Schema di stile abilitato per questo modulo" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "Lo schema di stile non è selezionato" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "Elenco degli schemi di stile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Anteprima degli schemi di stile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "Impostazioni degli schemi di stile" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "Lo stile verrà caricato nel tag " #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "Lo stile sarà caricato nel tag " #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Il successo" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "Supporto e KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Vista della tavoletta" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Colore del testo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Questo modulo è stilizzato con %s a livello globale, è " "possibile stilizzarlo con lo schema di stile corrente." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Questo modulo è stato creato con %s, è possibile creare uno " "stile con lo schema di stile corrente." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Questo modulo è stilizzato con lo schema di stile corrente; è possibile " "disattivarlo e utilizzare le impostazioni globali." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Questo modulo non ha uno schema di stile; è possibile attivare lo schema " "attuale per questo modulo o impostare uno schema di stile globale facendo " "clic su \"Usa per tutti i moduli\" sotto il titolo dello schema." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Questo schema non esiste" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Questo schema di stile è abilitato globalmente per tutti i moduli. Se si " "desidera utilizzare lo stile del proprio tema per il Modulo di contatto 7, " "fare clic sul pulsante \"Disattiva\"." #. Author of the plugin msgid "Tobias Conrad" msgstr "Tobias Conrad" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "Tutorial" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Senza stile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "Aggiornamento a Pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Carica immagine" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "Lunghezza verticale" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Benvenuti" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "Larghezza" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "Modulo di contatto in stile WOW 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "SÌ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "È possibile creare un nuovo schema di stile con le impostazioni dello schema " "corrente. Se si desidera creare uno schema di stile vuoto, deselezionare la " "casella di controllo sottostante." #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "Non è possibile eliminare" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "" "Non è possibile utilizzare queste impostazioni su questo tipo di pagina" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "Non si dispone di elementi del Modulo di contatto 7" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "Non si utilizza nessuno degli schemi di stile per il modulo di contatto 7. " "Fare clic su \"Usa per tutti i moduli\" per utilizzare lo schema corrente a " "livello globale." languages/cf7-styler-it_IT.mo000064400000047054147600046700012072 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'(#))**%*(*Y+ [+ e+q++k+,,,.,?,O,c,v,,h, , - (-#5- Y-f-m-v-}- -6- --- . (.D6.{/x00 0000&0*1 ?18`2 22&2222 3#373J3j3'3333 334 )4'64^4 15R51679999 ::%:::;(;=;Z;b; v;;; ;9;Q<7U<<+<0<= ==1=P=3=/->]>e>m>/? 8?F?b?w?~? ?(? ?????@'@)=@g@ o@{@@0@ @@@@A!A0(AYB iBvBB;BVB2CgDDIDDDX;E-E;ECE/BF.rF+F%FFG"0G'SG&{G G GGGGHuI|IJJ J K KK3KCK!WK yK KKKMLHhL3LL MMM`M- disable style scheme -- select -14-days Trial%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Language: it-IT POT-Creation-Date: 2023-04-15 15:26+0000 Plural-Forms: nplurals=2; plural=n != 1; PO-Revision-Date: 2023-04-15 16:11+0000 X-Generator: Loco https://localise.biz/ Project-Id-Version: WOW Style Contact Form 7 Report-Msgid-Bugs-To: Last-Translator: Tobias support@saleswonder.biz Language-Team: Italian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- disabilitare lo schema di stile -- selezionare -14 giorni di prova%s utilizzato per tutti i moduli a livello globale, fare clic su "Usa per tutti i moduli" se si desidera utilizzare lo schema corrente.Attivare lo stile per tutti i moduli*Attivare lo stile per il modulo correnteIn alternativa, è possibile disattivare la pulizia degli stili come in Thrive Architect:Colore BGOpacità BGColore di sfondoImmagine di sfondoPrima di utilizzare WOW Style Contact Form 7, è necessario installare e attivare il plugin Contact Form 7.Raggio di sfocaturaConfineColore del bordoRaggio del bordoTipo di confineLarghezza del bordoBordo del pulsantePulsantiDi Takayuki MiyoshiCF7 Customizer richiede che il plugin Contact Form 7 sia installato e attivo. È possibile scaricare %s.CF7 Il modulo non è selezionatoCF7 StylerAnnullamentoCaselle di controllo e radiobottoniStili pulitiLiberoChiudereColoreModulo di contatto 7 elencoContenereCopiare le impostazioni dello schema di stile correnteCopertinaCreareCreare più schemi di stileCreate un nuovo schema di stile!Stile attualeAttualmente il modulo %s è stilizzato con %s. Come nella versione gratuita, è possibile stilizzare solo un modulo alla volta e se si attiva lo stile per il modulo corrente, lo stile verrà rimosso dagli altri moduli.Attualmente il modulo %s è stilizzato con %s. Come nella versione gratuita, è possibile stilizzare solo un modulo alla volta e se si attiva lo stile per il modulo corrente, lo stile verrà rimosso dagli altri moduli.CSS personalizzatoCodice CSS personalizzatoTratteggiatoSchema predefinitoCancellare l'immagineVista sul desktopDisabilita lo stile per tutti i moduliDisabilita lo stile per il modulo correnteVuoi ottenere la versione premium di Styler gratuitamente? Allora inserisci la tua licenza WP2LEADS pro o ottieni una licenza qui!Volete salvare le modifiche prima di lasciare la pagina?A puntiniDoppioModulo duplicato nella seconda colonnaErroreUscita dallo schermo interoFamiglia di caratteriDimensione del carattereStile del caratterePeso del carattereImmagine BG del modulo e coloriPersonalizzazione del moduloImbottitura, margine e bordo del moduloTesto del moduloIl modulo non è selezionatoSchermo interoScanalaturaLunghezza orizzontaleColore del bordo del mouseColore HoverColore del testo al passaggio del mouseCome trasformare il vostro modulo contact form7 in un modulo di contatto che converte e che è facile da usare e in stile professionale, in un generatore di lead "survey" o in un modulo che cattura l'attenzioneSono felice di questo! RisparmioSe si utilizzano page builder come Thrive Architect, OptimizePress ecc., consultare la nostra pagina Base di conoscenza per risolvere eventuali problemiSe avete bisogno di caricare lo schema di stile all'interno del tag <body> solo su alcune pagine, potete farlo nel frontend utilizzando il pulsante "CF7 Styler". Questa funzione è disponibile solo per i tipi di post singoli (pagine, post, prodotti ecc.) e nella versione premium. Non è possibile farlo sulle pagine degli archivi (blog, elenco di prodotti, ecc.), in questo caso è necessario utilizzare le impostazioni globali.Se è necessario caricare lo schema di stile all'interno del tag <body> solo su alcune pagine, è possibile farlo nel frontend utilizzando il pulsante "CF7 Styler". Questa funzione è disponibile solo per i singoli tipi di post (pagine, post, prodotti ecc.). Non è possibile farlo sulle pagine degli archivi (blog, elenco di prodotti, ecc.), in questo caso è necessario utilizzare le impostazioni globali.Opacità dell'immaginePosizione dell'immagineDimensione dell'immagineLe impostazioni dello sfondo dell'immagine sono disponibili in modalità live solo per la versione Professional. È possibile provarle nella modalità di anteprima "stile corrente", ma non verranno salvate.EreditareCampi di inputTitolo dello schema di stile di inputInsertoCorsivoEtichette ColoreDimensione del carattere delle etichetteImpostazioni delle etichetteLicenzaAltezza della lineaLink ColoreColore del passaggio dei linkImpostazioni dei collegamentiIn direttaCaricamento degli stili nel tag <body>Caricare gli stili all'interno del tag <body> in questa paginaRendere la voce della casella di controllo una per rigaFare il pieno di larghezza?Rendere i campi di input a larghezza piena?Rendere l'elemento del radiobottone uno per rigaMargineVista mobileNONessun elemento di Contact Form 7 per l'anteprimaNessun modulo è stato creato con uno schema di stile, fare clic sul pulsante "Attiva lo stile per il modulo corrente" per applicare lo schema corrente a questo modulo.Non è stato selezionato alcun plugin da installareNon è stato selezionato alcuno schema di stileNormaleObliquoStili per riga per caselle di controllo e radiobottoni in modalità live solo per la versione Professional. È possibile provarlo in modalità anteprima "stile corrente", ma non verrà salvato.OpacitàStyler apertoOpt-in per vedere l'accountDimensione originaleSchemaEsternalizzazioneImbottituraInserire il titolo dello schema di stileAnteprimaAnteprima UnstyledModalità anteprimaRaggioRipetere entrambiRipetizione orizzontaleRipetizione verticaleRipristino delle impostazioni predefiniteCrinaleRisparmiareSalvatiVista della seconda colonnaSelezionare lo schema di stile per questo moduloImpostazioniImpostazioni salvate comeOmbraColore dell'ombraPosizione d'ombraSolidoAlcuni temi o page builder (ad esempio Thrive Architect, OptimizePress ecc.) potrebbero rimuovere gli stili in linea all'interno del tag <head>. Il caricamento dello schema di stile all'interno del tag <body> risolverà questo problema e mostrerà i moduli in stile.Modalità SplitVista divisaRaggio di diffusioneIniziare lo stylingIniziare con la creazione dello schema di stile predefinitoIniziate creando il vostro primo Contact Form 7Inizia la tua prova gratuita di 14 giorni con tutte le funzioni Professional qui!Passo 1. Fare clic sul link "Modifica con Thrive Architect" nell'elenco della pagina di amministrazionePasso 2. Fare clic sull'icona "Impostazioni" nel menu verticale di destraPasso 3. "Impostazioni avanzate" => "CSS nella sezione <head>"Passo 4. Assicurarsi che sia selezionata l'opzione "Non rimuovere i CSS da <head>"Passo 5. Fate clic sul pulsante "SALVA LAVOROModellare tutti i moduli con un solo clic a livello globaleModellare ogni modulo con qualsiasi schema di stile individualmenteSchema di stile disabilitato per tutti i moduliSchema di stile disabilitato per questo moduloSchema di stile abilitato per questo moduloLo schema di stile non è selezionatoElenco degli schemi di stileAnteprima degli schemi di stileImpostazioni degli schemi di stileLo stile verrà caricato nel tag Lo stile sarà caricato nel tag Il successoSupporto e KBVista della tavolettaColore del testoQuesto schema di stile è abilitato globalmente per tutti i moduli. Se si desidera utilizzare lo stile del proprio tema per il Modulo di contatto 7, fare clic sul pulsante "Disattiva".Questo modulo è stilizzato con %s a livello globale, è possibile stilizzarlo con lo schema di stile corrente.Questo modulo è stato creato con %s, è possibile creare uno stile con lo schema di stile corrente.Questo modulo è stilizzato con lo schema di stile corrente; è possibile disattivarlo e utilizzare le impostazioni globali.Questo modulo non ha uno schema di stile; è possibile attivare lo schema attuale per questo modulo o impostare uno schema di stile globale facendo clic su "Usa per tutti i moduli" sotto il titolo dello schema.Questo schema non esisteTobias ConradTutorialSenza stileAggiornamento a ProCarica immagineLunghezza verticaleModulo di contatto in stile WOW 7BenvenutiLarghezzaSÌÈ possibile creare un nuovo schema di stile con le impostazioni dello schema corrente. Se si desidera creare uno schema di stile vuoto, deselezionare la casella di controllo sottostante.Non è possibile eliminareNon è possibile utilizzare queste impostazioni su questo tipo di paginaNon si dispone di elementi del Modulo di contatto 7Non si utilizza nessuno degli schemi di stile per il modulo di contatto 7. Fare clic su "Usa per tutti i moduli" per utilizzare lo schema corrente a livello globale.cancellatoabilitato per tutti i modulihttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/languages/cf7-styler-hi_IN.po000064400000133766147600046700012061 0ustar00msgid "" msgstr "" "Language: hi-IN\n" "POT-Creation-Date: 2023-04-15 15:26+0000\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "PO-Revision-Date: 2023-04-15 16:10+0000\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: WOW Style Contact Form 7\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: Hindi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- डिस्काउंट स्टाइल योजना -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "चुनें -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14 दिन का मुकदमा" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%sवैश्विक स्तर पर सभी फॉर्मों के लिए उपयोग किया जाता है, " "यदि आप वर्तमान योजना का उपयोग करना चाहते हैं तो \"सभी फॉर्मों के लिए उपयोग " "करें\" पर क्लिक करें।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "सभी रूपों के लिए स्टाइल सक्रिय करें *" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "वर्तमान फॉर्म के लिए स्टाइल सक्रिय करें" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "एक विकल्प के रूप में आप फ्लोरिंग आर्किटेक्ट जैसे शैलियों को साफ करने के लिए " "अक्षम कर सकते हैं:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "पृष्ठभूमि रंग" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "पृष्ठभूमि छवि" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Wow स्टाइल संपर्क फ़ॉर्म 7 का उपयोग करने से पहले, आपको संपर्क फ़ॉर्म 7 " "प्लगइन स्थापित करने और सक्रिय करने की आवश्यकता है।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "बीजी रंग" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "BG अस्थिरता" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "ब्लू रेडिएशन" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "सीमाएं" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "सीमा का रंग" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "सीमा रेडिएशन" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "सीमा प्रकार" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "सीमा चौड़ा" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "बटन सीमा" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "बटन" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Takayuki Miyoshi के बारे में जानकारी" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "रद्द करें" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "Cf7 Customizer को स्थापित करने और सक्रिय होने के लिए संपर्क फॉर्म 7 प्लगइन " "की आवश्यकता होती है। आप% S डाउनलोड कर सकते हैं।" #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "Cf7 फ़ॉर्म नहीं चुना गया है" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "Cf7 स्टाइलर" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "चेकबॉक्स और रेडियोबॉटन" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "साफ स्टाइल" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "स्पष्ट" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "निकट" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "रंगों" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "संपर्क फ़ॉर्म 7 सूची" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "कंटेनर" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "वर्तमान स्टाइल योजना सेटिंग्स को कॉपी करें" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "कवर" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "बनाओ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "कई स्टाइल योजनाएं बनाएं" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "एक नया स्टाइल डिजाइन बनाएं!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "वर्तमान स्टाइल" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "वर्तमान में % एस आकार के " "साथ बनाया गया है % एस " ". मुफ्त संस्करण के रूप में आप एक ही समय में केवल एक फॉर्म स्टाइल कर " "सकते हैं और यदि आप वर्तमान फॉर्म के लिए स्टाइल सक्रिय करते हैं, तो स्टाइल को " "दूसरे फॉर्म से हटा दिया जाएगा।" #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "वर्तमान में % एस आकार के साथ बनाया गया है % एस " ". मुफ्त संस्करण के रूप में आप एक ही समय में केवल एक फॉर्म स्टाइल कर " "सकते हैं और यदि आप वर्तमान फॉर्म के लिए स्टाइल सक्रिय करते हैं, तो स्टाइल को " "दूसरे फॉर्म से हटा दिया जाएगा।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "सीएसएस के लिए" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "सीएसएस कोड" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "डैश" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "डिफ़ॉल्ट योजना" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "छवि को हटाना" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "हटाए गए" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "डेस्कटॉप दृश्य" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "सभी रूपों के लिए अक्षम स्टाइल" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "वर्तमान आकार के लिए अक्षम स्टाइल" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "क्या आप मुफ्त में स्टाइलर प्रीमियम संस्करण प्राप्त करना चाहते हैं? और फिर अपने Wp2Leads प्रो " "लाइसेंस दर्ज करें या एक लाइसेंस प्राप्त करें यहाँ " "!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "क्या आप पृष्ठ छोड़ने से पहले परिवर्तनों को बचाना चाहते हैं?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "ड्यूटी" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "दोगुना" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "दूसरी स्तंभ में डुप्लिकेट फॉर्म" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "सभी रूपों के लिए उपलब्ध है" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "गलतियां" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "पूर्ण स्क्रीन से बाहर" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "फ़ॉन्ट परिवार" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "फ़ॉन्ट आकार" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "फ़ॉन्ट स्टाइल" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "वजन लिपस्टिक" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "BG छवि और रंग" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "आकार के अनुकूलन" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "फॉर्म नहीं चुना गया है" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "फॉर्म पैडिंग, मार्जिन और सीमा" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "फॉर्म टेक्स्ट" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "पूर्ण स्क्रीन" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "बढ़ोतरी" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "क्षैतिज लंबाई" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Hover BG रंग" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "हॉवर्स रंग" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Hover पाठ रंग" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "अपने संपर्क फॉर्म 7 को एक रूपांतरण और उपयोग करने में आसान और प्रो स्टाइलिश " "संपर्क फॉर्म, \"अनुसंधान\" पाउडर जनरेटर या एक आंख पकड़ने फॉर्म में कैसे " "परिवर्तित करें" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "एचटीपीएस: //Saleswonder.Biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "Https: //Saleswonder.Biz/Blog/4Free-Contact-Form-7-Cf7-Formular-Und-Klick-" "Tipp-Einfach-Verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "मैं इस बात से खुश हूँ! बचाव" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "यदि आप पृष्ठ निर्माताओं का उपयोग कर रहे हैं, जैसे कि फ्रीव आर्किटेक्ट, " "Optimizepress आदि, कृपया हमारी जांच करें ज्ञान आधार पृष्ठ संभावित मुद्दों " "को हल करने के लिए" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "यदि आपको अंदर स्टाइल योजना लोड करने की आवश्यकता है केवल " "कुछ पृष्ठों पर टैग करें, आप \"Cf7 स्टाइलर\" बटन का उपयोग करके सामने पर ऐसा " "कर सकते हैं। यह सुविधा केवल एकल पोस्ट प्रकार (पृष्ठ, पोस्ट, उत्पाद, आदि) के " "लिए उपलब्ध है। और प्रीमियम संस्करण में। आप इसे संग्रह पृष्ठों (ब्लॉग, उत्पाद " "सूची आदि) पर नहीं कर सकते हैं, इस मामले में आपको वैश्विक सेटिंग्स का उपयोग " "करने की आवश्यकता है।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "यदि आपको अंदर स्टाइल योजना लोड करने की आवश्यकता है केवल " "कुछ पृष्ठों पर टैग करें, आप \"Cf7 स्टाइलर\" बटन का उपयोग करके सामने पर ऐसा " "कर सकते हैं। यह सुविधा केवल एकल पोस्ट प्रकार (पृष्ठ, पोस्ट, उत्पाद, आदि) के " "लिए उपलब्ध है। आप इसे संग्रह पृष्ठों (ब्लॉग, उत्पाद सूची आदि) पर नहीं कर " "सकते हैं, इस मामले में आपको वैश्विक सेटिंग्स का उपयोग करने की आवश्यकता है।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "छवि पृष्ठभूमि सेटिंग्स केवल पेशेवर संस्करण के लिए लाइव मोड में उपलब्ध हैं। " "आप इसे पूर्वावलोकन मोड \"वर्तमान शैली\" में परीक्षण कर सकते हैं, लेकिन यह " "बचाया नहीं जाएगा।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "तस्वीरें Opacity" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "छवि स्थिति" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "छवि आकार" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "विरासत" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "इनपुट के क्षेत्र" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "इनपुट स्टाइल प्रोग्राम शीर्षक" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "इंतजार" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "इटालियन" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "रंग लेबल" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "लेबल फ़ॉन्ट आकार" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "लेबल सेटिंग्स" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "अनुमति" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "लाइन ऊंचाई" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "बाएं रंग" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "बाईं ओर का रंग" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "बाएं सेटिंग्स" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "लाइव" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "लोड स्टाइल में Tag" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "आंतरिक लोड स्टाइल इस पृष्ठ पर टैग" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "चेकबॉक्स आइटम एक लाइन के लिए बनाएं" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "पूरी चौड़ाई बनाएं?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "इनपुट फ़ील्ड पूरी चौड़ाई बनाते हैं?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Radiobutton आइटम एक लाइन के लिए बनाएं" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "मार्जिन" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "मोबाइल दृश्य" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "नहीं नहीं" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "कोई संपर्क फॉर्म नहीं 7 वस्तुओं के लिए पूर्वावलोकन" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "कोई फॉर्म स्टाइल शेड्यूल के साथ स्टाइल नहीं है, इस फॉर्म के लिए वर्तमान " "शेड्यूल लागू करने के लिए \"वर्तमान फॉर्म के लिए स्टाइल सक्रिय करें\" बटन पर " "क्लिक करें।" #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "कोई प्लगइन स्थापित करने के लिए चुना नहीं है" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "कोई स्टाइल योजना नहीं चुनी गई" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "सामान्य" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Oblique में" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "चेकबॉक्स और रेडियोबॉटन के लिए लाइव मोड में केवल पेशेवर संस्करण के लिए एक-एक-" "लाइन स्टाइल। आप इसे पूर्वावलोकन मोड \"वर्तमान शैली\" में परीक्षण कर सकते हैं," " लेकिन यह बचाया नहीं जाएगा।" #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "अस्थिरता" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "खुले स्टाइल" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "अकाउंट देखने के लिए Opt-In" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "मूल आकार" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "आउटलिन" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "बाहर निकल" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "पद्मावती" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "कृपया स्टाइल प्रोग्राम का शीर्षक दर्ज करें" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "पूर्वावलोकन" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "पूर्वावलोकन मोड" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "अनौपचारिक पूर्वावलोकन" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "रेडिएशन" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "दोबारा दोहराएं" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "क्षैतिज दोहराएं" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "वर्चुअल पुनरावृत्ति" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "डिफ़ॉल्ट के लिए पुनरारंभ" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "रीज" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "बचाव" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "बचाए गए" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "दूसरा दृष्टिकोण" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "इस फॉर्म के लिए स्टाइल योजना चुनें" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "सेटिंग्स" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "सेटिंग्स को बचाया गया है" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "छाया" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "छाया का रंग" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "छाया की स्थिति" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "मजबूत" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "कुछ विषयों या पृष्ठ निर्माताओं (Fe. ट्रिप आर्किटेक्ट, Optimizepress आदि) " "आंतरिक स्टाइलों को हटा सकते हैं Tag है। आंतरिक लोड स्टाइल " "सिस्टम टैग इस समस्या को ठीक करेगा और आपके फॉर्म स्टाइलिश " "दिखाएगा।" #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "विभाजित मोड" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "विभाजित दृष्टि" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "रेडियो फैलाना" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "स्टाइलिंग शुरू करें" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "डिफ़ॉल्ट स्टाइल योजना बनाना शुरू करें" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "अपने आप को बनाने के साथ शुरू करें पहला " "संपर्क फॉर्म 7 " #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "सभी पेशेवर सुविधाओं के साथ अपने 14 दिन मुफ्त परीक्षण शुरू करें यहाँ !" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "चरण 1। Admin page list में \"edit with thrive architect\" लिंक पर क्लिक करें" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "चरण 2। सही ऊर्ध्वाधर मेनू पर \"सेटिंग्स\" आइकन पर क्लिक करें" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "चरण 3। \"उन्नत सेटिंग्स\" => \"सीएसएस में अनुभाग »" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "चरण 4। यह सुनिश्चित करें कि \"सीएसएस से बाहर न निकलें ' जांच की जा रही " "है" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "चरण 5। “Save Work” बटन पर क्लिक करें" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "वैश्विक स्तर पर एक क्लिक में सभी फॉर्म स्टाइल" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "स्टाइल प्रत्येक आकार किसी भी स्टाइल योजना के साथ व्यक्तिगत रूप से" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "स्टाइल योजना सभी रूपों के लिए अक्षम है" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "इस फॉर्म के लिए स्टाइल योजना अक्षम है" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "इस फॉर्म के लिए स्टाइल योजना सक्षम है" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "शैली का चयन नहीं किया गया है" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "शैली योजनाओं की सूची" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "स्टाइल प्रीव्यू" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "स्टाइल सिस्टम सेटिंग्स" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "स्टाइल में लोड किया जाएगा Tag" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "स्टाइल में लोड किया जाएगा Tag" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "सफलता" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "समर्थन और KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "टैबलेट दृश्य" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "रंग का पाठ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "इस फॉर्म के साथ स्टाइलिश % एस वैश्विक रूप से, आप इसे " "वर्तमान शैली योजना के साथ स्टाइल कर सकते हैं।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "इस फॉर्म के साथ स्टाइलिश % एस , आप इसे वर्तमान शैली योजना " "के साथ स्टाइल कर सकते हैं।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "यह फॉर्म वर्तमान शैली योजना के साथ स्टाइलिश है, आप इसे अक्षम कर सकते हैं और " "वैश्विक सेटिंग्स का उपयोग कर सकते हैं।" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "यह फॉर्म किसी भी शैली योजना के साथ स्टाइलिश नहीं है, आप इस फॉर्म के लिए " "वर्तमान योजना को सक्षम कर सकते हैं या \"सभी फॉर्मों के लिए उपयोग\" पर क्लिक " "करके वैश्विक शैली योजना सेट कर सकते हैं।" #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "इस योजना का अस्तित्व नहीं है" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "इस शैली योजना को वैश्विक रूप से सभी रूपों के लिए सक्षम किया गया है। यदि आप " "संपर्क फॉर्म के लिए अपने विषय के शैली का उपयोग करना चाहते हैं, तो 7 क्लिक " "करें \"अक्षम\" बटन।" #. Author of the plugin msgid "Tobias Conrad" msgstr "टॉबियास कॉनराड" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "ट्यूटोरियल" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "अनैच्छिक" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "Pro के लिए Upgrade" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Image अपलोड करें" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "ऊर्ध्वाधर लंबा" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "स्वागत है" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "विशाल" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "WOW स्टाइल संपर्क फॉर्म 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "हाँ हाँ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "आप वर्तमान योजना सेटिंग्स के साथ एक नया स्टाइल योजना बना सकते हैं। यदि आप " "नीचे सफेद शैली Schem Unchek चेकबॉक्स बनाना चाहते हैं।" #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "आप नहीं हटा सकते" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "आप इस पृष्ठ प्रकार पर इन सेटिंग्स का उपयोग नहीं कर सकते हैं" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "आपके पास कोई संपर्क फॉर्म नहीं है 7 आइटम" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "आप संपर्क फॉर्म 7 के लिए किसी भी स्टाइल योजना का उपयोग नहीं करते हैं। " "वैश्विक स्तर पर वर्तमान योजना का उपयोग करने के लिए \"सभी फॉर्मों के लिए " "उपयोग\" पर क्लिक करें।" languages/cf7-styler-hi_IN.mo000064400000071445147600046700012051 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'(B)*&%*L*_+i?,,--%-%-3."Q/t//"///0 0D&0 k0A111>2A2^2 q2~2422r2 I3 S3?`3G3(34R6#8#9 @9(J9 s9(9M9V :b:k<==U-==9=%==%>"?>b>)>M>%>:?%Y??%????@CAACG K@K]KtK#M,6MQcMMMM,M%"NHN[NxN$N%N N=Nj%OZO0O]PMzPP"PQQQq8SMSST TU V:*VeV|VVVrV!5W=WW+WW(W+X7,XBdX X XX+XZXYY@rY YY&YZZ\(<\%e\5\e\']i]vA__vM``L}awaBbdbaUcacHd6bd+d>dMeMReee"ee f gh(ijJl(lm;mTm"qm(m;mmn#n97n*qoof6pp>rDRr+rar- disable style scheme -- select -14-days Trial%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Language: hi-IN POT-Creation-Date: 2023-04-15 15:26+0000 Plural-Forms: nplurals=2; plural=n != 1; PO-Revision-Date: 2023-04-15 16:10+0000 X-Generator: Loco https://localise.biz/ Project-Id-Version: WOW Style Contact Form 7 Report-Msgid-Bugs-To: Last-Translator: Tobias support@saleswonder.biz Language-Team: Hindi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- डिस्काउंट स्टाइल योजना -चुनें -14 दिन का मुकदमा%sवैश्विक स्तर पर सभी फॉर्मों के लिए उपयोग किया जाता है, यदि आप वर्तमान योजना का उपयोग करना चाहते हैं तो "सभी फॉर्मों के लिए उपयोग करें" पर क्लिक करें।सभी रूपों के लिए स्टाइल सक्रिय करें *वर्तमान फॉर्म के लिए स्टाइल सक्रिय करेंएक विकल्प के रूप में आप फ्लोरिंग आर्किटेक्ट जैसे शैलियों को साफ करने के लिए अक्षम कर सकते हैं:बीजी रंगBG अस्थिरतापृष्ठभूमि रंगपृष्ठभूमि छविWow स्टाइल संपर्क फ़ॉर्म 7 का उपयोग करने से पहले, आपको संपर्क फ़ॉर्म 7 प्लगइन स्थापित करने और सक्रिय करने की आवश्यकता है।ब्लू रेडिएशनसीमाएंसीमा का रंगसीमा रेडिएशनसीमा प्रकारसीमा चौड़ाबटन सीमाबटनTakayuki Miyoshi के बारे में जानकारीCf7 Customizer को स्थापित करने और सक्रिय होने के लिए संपर्क फॉर्म 7 प्लगइन की आवश्यकता होती है। आप% S डाउनलोड कर सकते हैं।Cf7 फ़ॉर्म नहीं चुना गया हैCf7 स्टाइलररद्द करेंचेकबॉक्स और रेडियोबॉटनसाफ स्टाइलस्पष्टनिकटरंगोंसंपर्क फ़ॉर्म 7 सूचीकंटेनरवर्तमान स्टाइल योजना सेटिंग्स को कॉपी करेंकवरबनाओकई स्टाइल योजनाएं बनाएंएक नया स्टाइल डिजाइन बनाएं!वर्तमान स्टाइलवर्तमान में % एस आकार के साथ बनाया गया है % एस . मुफ्त संस्करण के रूप में आप एक ही समय में केवल एक फॉर्म स्टाइल कर सकते हैं और यदि आप वर्तमान फॉर्म के लिए स्टाइल सक्रिय करते हैं, तो स्टाइल को दूसरे फॉर्म से हटा दिया जाएगा।वर्तमान में % एस आकार के साथ बनाया गया है % एस . मुफ्त संस्करण के रूप में आप एक ही समय में केवल एक फॉर्म स्टाइल कर सकते हैं और यदि आप वर्तमान फॉर्म के लिए स्टाइल सक्रिय करते हैं, तो स्टाइल को दूसरे फॉर्म से हटा दिया जाएगा।सीएसएस के लिएसीएसएस कोडडैशडिफ़ॉल्ट योजनाछवि को हटानाडेस्कटॉप दृश्यसभी रूपों के लिए अक्षम स्टाइलवर्तमान आकार के लिए अक्षम स्टाइलक्या आप मुफ्त में स्टाइलर प्रीमियम संस्करण प्राप्त करना चाहते हैं? और फिर अपने Wp2Leads प्रो लाइसेंस दर्ज करें या एक लाइसेंस प्राप्त करें यहाँ !क्या आप पृष्ठ छोड़ने से पहले परिवर्तनों को बचाना चाहते हैं?ड्यूटीदोगुनादूसरी स्तंभ में डुप्लिकेट फॉर्मगलतियांपूर्ण स्क्रीन से बाहरफ़ॉन्ट परिवारफ़ॉन्ट आकारफ़ॉन्ट स्टाइलवजन लिपस्टिकBG छवि और रंगआकार के अनुकूलनफॉर्म पैडिंग, मार्जिन और सीमाफॉर्म टेक्स्टफॉर्म नहीं चुना गया हैपूर्ण स्क्रीनबढ़ोतरीक्षैतिज लंबाईHover BG रंगहॉवर्स रंगHover पाठ रंगअपने संपर्क फॉर्म 7 को एक रूपांतरण और उपयोग करने में आसान और प्रो स्टाइलिश संपर्क फॉर्म, "अनुसंधान" पाउडर जनरेटर या एक आंख पकड़ने फॉर्म में कैसे परिवर्तित करेंमैं इस बात से खुश हूँ! बचावयदि आप पृष्ठ निर्माताओं का उपयोग कर रहे हैं, जैसे कि फ्रीव आर्किटेक्ट, Optimizepress आदि, कृपया हमारी जांच करें ज्ञान आधार पृष्ठ संभावित मुद्दों को हल करने के लिएयदि आपको अंदर स्टाइल योजना लोड करने की आवश्यकता है केवल कुछ पृष्ठों पर टैग करें, आप "Cf7 स्टाइलर" बटन का उपयोग करके सामने पर ऐसा कर सकते हैं। यह सुविधा केवल एकल पोस्ट प्रकार (पृष्ठ, पोस्ट, उत्पाद, आदि) के लिए उपलब्ध है। और प्रीमियम संस्करण में। आप इसे संग्रह पृष्ठों (ब्लॉग, उत्पाद सूची आदि) पर नहीं कर सकते हैं, इस मामले में आपको वैश्विक सेटिंग्स का उपयोग करने की आवश्यकता है।यदि आपको अंदर स्टाइल योजना लोड करने की आवश्यकता है केवल कुछ पृष्ठों पर टैग करें, आप "Cf7 स्टाइलर" बटन का उपयोग करके सामने पर ऐसा कर सकते हैं। यह सुविधा केवल एकल पोस्ट प्रकार (पृष्ठ, पोस्ट, उत्पाद, आदि) के लिए उपलब्ध है। आप इसे संग्रह पृष्ठों (ब्लॉग, उत्पाद सूची आदि) पर नहीं कर सकते हैं, इस मामले में आपको वैश्विक सेटिंग्स का उपयोग करने की आवश्यकता है।तस्वीरें Opacityछवि स्थितिछवि आकारछवि पृष्ठभूमि सेटिंग्स केवल पेशेवर संस्करण के लिए लाइव मोड में उपलब्ध हैं। आप इसे पूर्वावलोकन मोड "वर्तमान शैली" में परीक्षण कर सकते हैं, लेकिन यह बचाया नहीं जाएगा।विरासतइनपुट के क्षेत्रइनपुट स्टाइल प्रोग्राम शीर्षकइंतजारइटालियनरंग लेबललेबल फ़ॉन्ट आकारलेबल सेटिंग्सअनुमतिलाइन ऊंचाईबाएं रंगबाईं ओर का रंगबाएं सेटिंग्सलाइवलोड स्टाइल में Tagआंतरिक लोड स्टाइल इस पृष्ठ पर टैगचेकबॉक्स आइटम एक लाइन के लिए बनाएंपूरी चौड़ाई बनाएं?इनपुट फ़ील्ड पूरी चौड़ाई बनाते हैं?Radiobutton आइटम एक लाइन के लिए बनाएंमार्जिनमोबाइल दृश्यनहीं नहींकोई संपर्क फॉर्म नहीं 7 वस्तुओं के लिए पूर्वावलोकनकोई फॉर्म स्टाइल शेड्यूल के साथ स्टाइल नहीं है, इस फॉर्म के लिए वर्तमान शेड्यूल लागू करने के लिए "वर्तमान फॉर्म के लिए स्टाइल सक्रिय करें" बटन पर क्लिक करें।कोई प्लगइन स्थापित करने के लिए चुना नहीं हैकोई स्टाइल योजना नहीं चुनी गईसामान्यOblique मेंचेकबॉक्स और रेडियोबॉटन के लिए लाइव मोड में केवल पेशेवर संस्करण के लिए एक-एक-लाइन स्टाइल। आप इसे पूर्वावलोकन मोड "वर्तमान शैली" में परीक्षण कर सकते हैं, लेकिन यह बचाया नहीं जाएगा।अस्थिरताखुले स्टाइलअकाउंट देखने के लिए Opt-Inमूल आकारआउटलिनबाहर निकलपद्मावतीकृपया स्टाइल प्रोग्राम का शीर्षक दर्ज करेंपूर्वावलोकनअनौपचारिक पूर्वावलोकनपूर्वावलोकन मोडरेडिएशनदोबारा दोहराएंक्षैतिज दोहराएंवर्चुअल पुनरावृत्तिडिफ़ॉल्ट के लिए पुनरारंभरीजबचावबचाए गएदूसरा दृष्टिकोणइस फॉर्म के लिए स्टाइल योजना चुनेंसेटिंग्ससेटिंग्स को बचाया गया हैछायाछाया का रंगछाया की स्थितिमजबूतकुछ विषयों या पृष्ठ निर्माताओं (Fe. ट्रिप आर्किटेक्ट, Optimizepress आदि) आंतरिक स्टाइलों को हटा सकते हैं Tag है। आंतरिक लोड स्टाइल सिस्टम टैग इस समस्या को ठीक करेगा और आपके फॉर्म स्टाइलिश दिखाएगा।विभाजित मोडविभाजित दृष्टिरेडियो फैलानास्टाइलिंग शुरू करेंडिफ़ॉल्ट स्टाइल योजना बनाना शुरू करेंअपने आप को बनाने के साथ शुरू करें पहला संपर्क फॉर्म 7 सभी पेशेवर सुविधाओं के साथ अपने 14 दिन मुफ्त परीक्षण शुरू करें यहाँ !चरण 1। Admin page list में "edit with thrive architect" लिंक पर क्लिक करेंचरण 2। सही ऊर्ध्वाधर मेनू पर "सेटिंग्स" आइकन पर क्लिक करेंचरण 3। "उन्नत सेटिंग्स" => "सीएसएस में अनुभाग »चरण 4। यह सुनिश्चित करें कि "सीएसएस से बाहर न निकलें ' जांच की जा रही हैचरण 5। “Save Work” बटन पर क्लिक करेंवैश्विक स्तर पर एक क्लिक में सभी फॉर्म स्टाइलस्टाइल प्रत्येक आकार किसी भी स्टाइल योजना के साथ व्यक्तिगत रूप सेस्टाइल योजना सभी रूपों के लिए अक्षम हैइस फॉर्म के लिए स्टाइल योजना अक्षम हैइस फॉर्म के लिए स्टाइल योजना सक्षम हैशैली का चयन नहीं किया गया हैशैली योजनाओं की सूचीस्टाइल प्रीव्यूस्टाइल सिस्टम सेटिंग्सस्टाइल में लोड किया जाएगा Tagस्टाइल में लोड किया जाएगा Tagसफलतासमर्थन और KBटैबलेट दृश्यरंग का पाठइस शैली योजना को वैश्विक रूप से सभी रूपों के लिए सक्षम किया गया है। यदि आप संपर्क फॉर्म के लिए अपने विषय के शैली का उपयोग करना चाहते हैं, तो 7 क्लिक करें "अक्षम" बटन।इस फॉर्म के साथ स्टाइलिश % एस वैश्विक रूप से, आप इसे वर्तमान शैली योजना के साथ स्टाइल कर सकते हैं।इस फॉर्म के साथ स्टाइलिश % एस , आप इसे वर्तमान शैली योजना के साथ स्टाइल कर सकते हैं।यह फॉर्म वर्तमान शैली योजना के साथ स्टाइलिश है, आप इसे अक्षम कर सकते हैं और वैश्विक सेटिंग्स का उपयोग कर सकते हैं।यह फॉर्म किसी भी शैली योजना के साथ स्टाइलिश नहीं है, आप इस फॉर्म के लिए वर्तमान योजना को सक्षम कर सकते हैं या "सभी फॉर्मों के लिए उपयोग" पर क्लिक करके वैश्विक शैली योजना सेट कर सकते हैं।इस योजना का अस्तित्व नहीं हैटॉबियास कॉनराडट्यूटोरियलअनैच्छिकPro के लिए UpgradeImage अपलोड करेंऊर्ध्वाधर लंबाWOW स्टाइल संपर्क फॉर्म 7स्वागत हैविशालहाँ हाँआप वर्तमान योजना सेटिंग्स के साथ एक नया स्टाइल योजना बना सकते हैं। यदि आप नीचे सफेद शैली Schem Unchek चेकबॉक्स बनाना चाहते हैं।आप नहीं हटा सकतेआप इस पृष्ठ प्रकार पर इन सेटिंग्स का उपयोग नहीं कर सकते हैंआपके पास कोई संपर्क फॉर्म नहीं है 7 आइटमआप संपर्क फॉर्म 7 के लिए किसी भी स्टाइल योजना का उपयोग नहीं करते हैं। वैश्विक स्तर पर वर्तमान योजना का उपयोग करने के लिए "सभी फॉर्मों के लिए उपयोग" पर क्लिक करें।हटाए गएसभी रूपों के लिए उपलब्ध हैएचटीपीएस: //Saleswonder.BizHttps: //Saleswonder.Biz/Blog/4Free-Contact-Form-7-Cf7-Formular-Und-Klick-Tipp-Einfach-Verbinden/languages/cf7-styler-fr_FR.po000064400000115466147600046700012066 0ustar00msgid "" msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: Wow style (ES)\n" "Language: fr-FR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "POT-Creation-Date: 2020-04-18 12:29+0000\n" "PO-Revision-Date: 2023-04-15 16:08+0000\n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: French (France)\n" "Report-Msgid-Bugs-To: \n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- désactiver le thème de style -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "- choisir -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "Période d'essai de 14 jours" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #| msgid "" #| "%s used for all forms globally, click \"Use for all forms\" if you want to " #| "use current Scheme." msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s utilisés globalement pour tous les formulaires, cliquez sur « Utiliser " "pour tous les formulaires » si vous souhaitez utiliser le Thème actuel." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Activez le style pour tous les formulaires*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Activer le style pour le formulaire actuel" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "En option, vous pouvez désactiver le nettoyage de styles, comme dans le plug-" "in Thrive Architect :" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Couleur de fond" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Image de fond" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Avant d'utiliser l’extension WOW Style Contact Form 7, vous devez installer " "et activer l’extension Contact Form 7." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "Couleur de fond" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "Transparence de fond" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Rayon de flou" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "Bordure" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Couleur de la bordure" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Rayon de la bordure" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Type de la bordure" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "Largeur de la bordure" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "Bordure de bouton" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Boutons" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Auteur : Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "Annuler" #: cf7-styler.php:129 msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "L’extension pour modifier les styles du Contact Form 7 nécessite " "l'installation et l'activation de l’extension du formulaire Contact Form 7. " "Vous pouvez télécharger %s." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "Le formulaire CF7 n'est pas sélectionné" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7 Styler" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Champs de sélection multiples et uniques" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "Nettoyer les styles" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "Nettoyer" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "Fermer" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Couleur" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Liste des formulaires CF7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "Contenir" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Copier les paramètres de le thème de style actuel" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "Couvrir" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "Créer" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Créer plusieurs thèmes de style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Créer un nouveau thème de style!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Style actuel" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "Actuellement, le formulaire " "%s est stylé avec " "%s. Comme dans la version gratuite, vous ne pouvez styler qu'un " "seul formulaire à la fois et si vous activez le style pour le formulaire en " "cours, le style sera supprimé pour les autres formulaires." #: admin/class-cf7-customizer-admin.php:481 #| msgid "" #| "Currently %s form is styled with %s. As in free version you can style only " #| "one form at a time and if you activate style for current form, style will be " #| "removed from other form." msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "Actuellement, le formulaire %s est stylé avec " "%s. Comme dans la version gratuite, vous ne pouvez styler qu'un " "seul formulaire à la fois et si vous activez le style pour le formulaire " "actuel, le style sera supprimé pour les autres formulaires." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "CSS personnalisé" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "Code CSS personnalisé" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Un trait en pointillés (tirets)" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Thème par défaut" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "Supprimer l'image" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "supprimée" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Vue de bureau" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Désactiver le style pour tous les formulaires" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Désactiver le style du formulaire actuel" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "Voulez-vous obtenir la version premium de styler gratuitement ? Alors Entrez votre " "licence WP2LEADS pro ou obtenez une licence " "ici !" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "" "Souhaitez-vous enregistrer vos modifications avant de quitter la page ?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "Un trait en pointillés (points)" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Trait double" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Dupliquer le formulaire dans la deuxième colonne" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "activé pour tous les formulaires" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Erreur" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Sortir du plein écran" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Famille de polices" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "Taille de police" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Style de police" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "Poids de police" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Image et couleurs de fond du formulaire" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Personnalisation du formulaire" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Aucun formulaire n'a été sélectionné" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Remplissage, marge et bordure du formulaire" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Texte du formulaire" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "Plein écran" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "En relief" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Longueur horizontale" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Couleur de fond survolé" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "Couleur survolée" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Couleur du texte survolé" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Comment créer un formulaire de contact Contact Form 7, qui non seulement " "aura un aspect professionnel et attrayant et sera facile à utiliser, mais " "qui aura également un taux de conversion élevé et générera des demandes de " "renseignements sur les offres" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "https://saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "J'aime ça ! Sauvegarder" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Si vous utilisez des constructeurs de pages, comme Thrive Architect, " "OptimizePress etc., veuillez consulter notre Page de la base de connaissances " "pour résoudre les problèmes éventuels" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Si vous avez besoin de charger un schéma de style à l'intérieur de la balise " "<body> uniquement sur certaines pages, vous pouvez le " "faire sur le frontend en utilisant le bouton \"CF7 Styler\". Cette fonction " "n'est disponible que pour les types d'articles (pages, articles, produits, " "etc.) et dans la version premium. Vous ne pouvez pas le faire sur les pages " "d'archives (blog, liste de produits etc.) dans ce cas vous devez utiliser " "les paramètres globaux." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Si vous avez besoin de charger un schéma de style à l'intérieur de la balise " "<body> uniquement sur certaines pages, vous pouvez le " "faire sur le frontend en utilisant le bouton \"CF7 Styler\". Cette fonction " "n'est disponible que pour les types d'articles (pages, articles, produits, " "etc.). Vous ne pouvez pas le faire sur les pages d'archives (blog, liste de " "produits etc.) dans ce cas vous devez utiliser les paramètres globaux." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "Les paramètres de l'image de fond ne sont disponibles que pour la version " "Professional en mode temps réel. Vous pouvez les essayer dans le mode de " "prévisualisation « style actuel », mais ils ne seront pas enregistrés." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Transparence de l'image" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Position de l'image" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "Taille de l'image" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Hériter" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Champs de saisie" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Nom du thème de style de saisie" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Effet 3D global enfoncé" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Italique" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Couleur des étiquettes" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Taille de police de l'étiquette" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Paramètres de l'étiquette" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Licence" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "Hauteur de la ligne" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Couleur des liens" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Couleur des liens survolés" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Paramètres des liens" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "En direct" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Charger les styles dans la balise <body>" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "" "Charger les styles à l'intérieur de la balise <body> sur " "cette page" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Définir un champ à choix multiple pour chaque ligne" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "Fixer la pleine largeur?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "Configurer les champs d'entrée sur toute leur largeur?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Définir un champ de sélection unique dans chaque ligne" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Marge" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Vue dans la cellule" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "NON" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "Aucun élément du formulaire Contact Form 7 à visualiser" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "Aucun formulaire n'est modifié avec le thème de style. Pour appliquer le " "thème de style actuelle à ce formulaire, cliquez sur « Activer le style du " "formulaire actuel »." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "Aucune extension n'est sélectionnée pour l'installation" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "Aucun thème de style n'a été sélectionné" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "Normal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Oblique" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "La définition d'un champ de sélection multiple et d'un champ de sélection " "unique n'est possible que dans la version Professional. Vous pouvez " "l’essayer dans le mode de prévisualisation « style actuel », mais elle ne " "sera pas enregistrée." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Transparence" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Ouvrez le Styler" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "S'inscrire pour voir le compte" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "Taille originale" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Contour" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "Effet 3D global surélevé" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Remplissage" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Entrez un thème de style de saisie" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Aperçu" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "Mode d’aperçu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Aperçu du style non modifié" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Rayon" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "Répéter les deux" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "Répéter horizontalement" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "Répéter verticalement" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Rétablir les paramètres par défaut" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Un trait avec effet 3D: bourrelet" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "Sauvegarder" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "Sauvegardé" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Vue de la deuxième colonne" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Sélectionner un thème de style pour ce formulaire" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Paramètres" #: admin/class-cf7-customizer-admin-ajax.php:210 #| msgid "Settings saved as" msgid "Settings saved as " msgstr "Paramètres enregistrés sous" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "Ombre" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "Couleur de l’ombre" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Position de l'ombre" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Trait plein" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 #| msgid "" #| "Some themes or page builders (e.g. Thrive Architect, OptimizePress etc.) " #| "could remove inline styles inside tag. Loading style scheme within " #| " tag will fix this issue and show your forms styled." msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Certains motifs ou assistants de création de sites (par exemple Thrive " "Architect, OptimizePress, etc.) peuvent supprimer les styles locaux (inline " "styles) à l'intérieur de la balise . L'ajout d'un thème de style à " "l'intérieur de la balise résoudra ce problème et vos formulaires " "auront un style modifié." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "Mode de vue fractionnée" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "Vue fractionnée" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Rayon de diffusion" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Commencez à modifier le style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Commencez par créer un thème de style par défaut" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Commencez par créer votre premier " "Contact Form 7" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "Commencez votre essai gratuit de 14 jours avec toutes les fonctions " "professionnelles ici !" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Étape 1 : Cliquez sur le lien « Edit with Thrive Architect » (Modifier avec " "Thrive Architect) dans la liste des pages administrateur" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "" "Étape 2 : Cliquez sur l'icône « Settings » (Paramètres) dans le menu " "vertical de droite" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 #| msgid "Step 3. \"Advanced settings\" => \"CSS in the section\"" msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" "Étape 3 : « Advanced settings » (Paramètres avancés) => « CSS in the " "section» (CSS dans la section )" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 #| msgid "Step 4. Make sure that \"Do not strip CSS from \" is checked" msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "Étape 4 : Assurez-vous que l'option « Do not strip CSS from » (Ne pas " "supprimer le CSS de la section ) est cochée" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Étape 5 : Cliquez sur le bouton « SAVE WORK » (SAUVEGARDER LE TRAVAIL)" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "Modifiez le style de tous les formulaires globalement en un seul clic" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "" "Modifiez le style de chaque formulaire individuellement en utilisant " "n'importe quel thème" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Le thème de style est désactivé pour tous les formulaires" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Le thème de style est désactivé pour ce formulaire" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Le thème de style est activé pour ce formulaire" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "Le thème de style n'est pas choisi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "Liste des thèmes de style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Aperçu des thèmes de style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "Paramètres des thèmes de style" #: admin/class-cf7-customizer-admin-ajax.php:298 #| msgid "Style will be loaded in tag" msgid "Style will be loaded in tag" msgstr "Le style sera chargé dans la balise" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "Le style sera chargé dans la balise " #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Effectué avec succès" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "Support & KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Vue dans la tablette" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Couleur du texte" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #| msgid "" #| "This form is styled with %s globally, you can style it with current Style " #| "scheme." msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Le style de ce formulaire est modifié globalement avec %s, vous pouvez le " "modifier avec le thème de style actuel." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #| msgid "" #| "This form is styled with %s, you can style it with current Style scheme." msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Le style de ce formulaire est modifié avec %s, vous pouvez le modifier avec " "le thème de style actuel." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Le style de ce formulaire est modifié avec le Thème de style actuel, vous " "pouvez le désactiver et utiliser les paramètres globaux." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Le style de ce formulaire n'est modifié par aucun thème de style, vous " "pouvez soit activer le thème actuel pour ce formulaire, soit définir le " "modèle de style global en cliquant sur « Utiliser pour tous les formulaires " "» sous le nom du thème." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Ce thème n'existe pas" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Ce thème de style est activé globalement pour tous les formulaires. Si vous " "souhaitez utiliser un style de votre motif pour le formulaire Contact Form 7," " cliquez sur le bouton « Désactiver »." #. Author of the plugin msgid "Tobias Conrad" msgstr "Tobias Conrad" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "Tutoriel" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Style non modifié" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "Mise à jour vers la version Pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Charger l'image" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "Longueur verticale" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Bienvenu" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "Largeur" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "WOW Style Contact Form 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "OUI" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 #| msgid "" #| "You can create new style scheme with current scheme settings. If you want to " #| "create blank style scheme uncheck checkbox below." msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "Vous pouvez créer un nouveau thème de style avec les paramètres du thème " "actuel. Si vous souhaitez créer un thème de style vide, décochez la case ci-" "dessous." #: admin/class-cf7-customizer-admin-ajax.php:237 #| msgid "You cannot delete" msgid "You can not delete" msgstr "Vous ne pouvez pas supprimer" #: public/class-cf7-customizer-public.php:471 #| msgid "You cannot use this settings on this page type" msgid "You can not use this settings on this page type" msgstr "" "Vous ne pouvez pas utiliser ces paramètres sur des pages comme celle-ci" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "Vous n'avez pas d'éléments de formulaire Contact Form 7" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "Vous n'utilisez aucun thème de style pour le formulaire Contact Form 7. " "Cliquez sur « Utiliser pour tous les formulaires » pour utiliser la thème " "actuel de façon globale." languages/cf7-styler-fr_FR.mo000064400000050650147600046700012054 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'(") )**+***c +n+~++ +v+ (,6,>,T,h,{,,,,,)u- --)----..".3+._.g.!n.". .a."0:1L1 c111 1.1)1"2G33 {3 313333 44-4'=4e4+44(4 4 45525D5^5a6z6|7Y9;3;G;Y;9<B< S<t<<< <<<<==5= K=;U=X=5= >79>8q>>>>:>?9?-?@!@)@ A-A>A]AnAvA A#AAAAAAB+B%CB!iB B BB3B BBC#C8C LCEXCDDDD3DX.E&EF\6GxG HIHEHZI<zI5I1I#JCJ^J {J$J+JJ KK&K7KsKgpLL_MZN qNNN NNNNNO O OOHO9PRP Q! Q/Q`GQ- disable style scheme -- select -14-days Trial%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Loco https://localise.biz/ Project-Id-Version: Wow style (ES) Language: fr-FR Plural-Forms: nplurals=2; plural=(n > 1); POT-Creation-Date: 2020-04-18 12:29+0000 PO-Revision-Date: 2023-04-15 16:08+0000 Last-Translator: Tobias support@saleswonder.biz Language-Team: French (France) Report-Msgid-Bugs-To: X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- désactiver le thème de style -- choisir -Période d'essai de 14 jours%s utilisés globalement pour tous les formulaires, cliquez sur « Utiliser pour tous les formulaires » si vous souhaitez utiliser le Thème actuel.Activez le style pour tous les formulaires*Activer le style pour le formulaire actuelEn option, vous pouvez désactiver le nettoyage de styles, comme dans le plug-in Thrive Architect :Couleur de fondTransparence de fondCouleur de fondImage de fondAvant d'utiliser l’extension WOW Style Contact Form 7, vous devez installer et activer l’extension Contact Form 7.Rayon de flouBordureCouleur de la bordureRayon de la bordureType de la bordureLargeur de la bordureBordure de boutonBoutonsAuteur : Takayuki MiyoshiL’extension pour modifier les styles du Contact Form 7 nécessite l'installation et l'activation de l’extension du formulaire Contact Form 7. Vous pouvez télécharger %s.Le formulaire CF7 n'est pas sélectionnéCF7 StylerAnnulerChamps de sélection multiples et uniquesNettoyer les stylesNettoyerFermerCouleurListe des formulaires CF7ContenirCopier les paramètres de le thème de style actuelCouvrirCréerCréer plusieurs thèmes de styleCréer un nouveau thème de style!Style actuelActuellement, le formulaire %s est stylé avec %s. Comme dans la version gratuite, vous ne pouvez styler qu'un seul formulaire à la fois et si vous activez le style pour le formulaire en cours, le style sera supprimé pour les autres formulaires.Actuellement, le formulaire %s est stylé avec %s. Comme dans la version gratuite, vous ne pouvez styler qu'un seul formulaire à la fois et si vous activez le style pour le formulaire actuel, le style sera supprimé pour les autres formulaires.CSS personnaliséCode CSS personnaliséUn trait en pointillés (tirets)Thème par défautSupprimer l'imageVue de bureauDésactiver le style pour tous les formulairesDésactiver le style du formulaire actuelVoulez-vous obtenir la version premium de styler gratuitement ? Alors Entrez votre licence WP2LEADS pro ou obtenez une licence ici !Souhaitez-vous enregistrer vos modifications avant de quitter la page ?Un trait en pointillés (points)Trait doubleDupliquer le formulaire dans la deuxième colonneErreurSortir du plein écranFamille de policesTaille de policeStyle de policePoids de policeImage et couleurs de fond du formulairePersonnalisation du formulaireRemplissage, marge et bordure du formulaireTexte du formulaireAucun formulaire n'a été sélectionnéPlein écranEn reliefLongueur horizontaleCouleur de fond survoléCouleur survoléeCouleur du texte survoléComment créer un formulaire de contact Contact Form 7, qui non seulement aura un aspect professionnel et attrayant et sera facile à utiliser, mais qui aura également un taux de conversion élevé et générera des demandes de renseignements sur les offresJ'aime ça ! SauvegarderSi vous utilisez des constructeurs de pages, comme Thrive Architect, OptimizePress etc., veuillez consulter notre Page de la base de connaissances pour résoudre les problèmes éventuelsSi vous avez besoin de charger un schéma de style à l'intérieur de la balise <body> uniquement sur certaines pages, vous pouvez le faire sur le frontend en utilisant le bouton "CF7 Styler". Cette fonction n'est disponible que pour les types d'articles (pages, articles, produits, etc.) et dans la version premium. Vous ne pouvez pas le faire sur les pages d'archives (blog, liste de produits etc.) dans ce cas vous devez utiliser les paramètres globaux.Si vous avez besoin de charger un schéma de style à l'intérieur de la balise <body> uniquement sur certaines pages, vous pouvez le faire sur le frontend en utilisant le bouton "CF7 Styler". Cette fonction n'est disponible que pour les types d'articles (pages, articles, produits, etc.). Vous ne pouvez pas le faire sur les pages d'archives (blog, liste de produits etc.) dans ce cas vous devez utiliser les paramètres globaux.Transparence de l'imagePosition de l'imageTaille de l'imageLes paramètres de l'image de fond ne sont disponibles que pour la version Professional en mode temps réel. Vous pouvez les essayer dans le mode de prévisualisation « style actuel », mais ils ne seront pas enregistrés.HériterChamps de saisieNom du thème de style de saisieEffet 3D global enfoncéItaliqueCouleur des étiquettesTaille de police de l'étiquetteParamètres de l'étiquetteLicenceHauteur de la ligneCouleur des liensCouleur des liens survolésParamètres des liensEn directCharger les styles dans la balise <body>Charger les styles à l'intérieur de la balise <body> sur cette pageDéfinir un champ à choix multiple pour chaque ligneFixer la pleine largeur?Configurer les champs d'entrée sur toute leur largeur?Définir un champ de sélection unique dans chaque ligneMargeVue dans la celluleNONAucun élément du formulaire Contact Form 7 à visualiserAucun formulaire n'est modifié avec le thème de style. Pour appliquer le thème de style actuelle à ce formulaire, cliquez sur « Activer le style du formulaire actuel ».Aucune extension n'est sélectionnée pour l'installationAucun thème de style n'a été sélectionnéNormalObliqueLa définition d'un champ de sélection multiple et d'un champ de sélection unique n'est possible que dans la version Professional. Vous pouvez l’essayer dans le mode de prévisualisation « style actuel », mais elle ne sera pas enregistrée.TransparenceOuvrez le StylerS'inscrire pour voir le compteTaille originaleContourEffet 3D global surélevéRemplissageEntrez un thème de style de saisieAperçuAperçu du style non modifiéMode d’aperçuRayonRépéter les deuxRépéter horizontalementRépéter verticalementRétablir les paramètres par défautUn trait avec effet 3D: bourreletSauvegarderSauvegardéVue de la deuxième colonneSélectionner un thème de style pour ce formulaireParamètresParamètres enregistrés sousOmbreCouleur de l’ombrePosition de l'ombreTrait pleinCertains motifs ou assistants de création de sites (par exemple Thrive Architect, OptimizePress, etc.) peuvent supprimer les styles locaux (inline styles) à l'intérieur de la balise . L'ajout d'un thème de style à l'intérieur de la balise résoudra ce problème et vos formulaires auront un style modifié.Mode de vue fractionnéeVue fractionnéeRayon de diffusionCommencez à modifier le styleCommencez par créer un thème de style par défautCommencez par créer votre premier Contact Form 7Commencez votre essai gratuit de 14 jours avec toutes les fonctions professionnelles ici !Étape 1 : Cliquez sur le lien « Edit with Thrive Architect » (Modifier avec Thrive Architect) dans la liste des pages administrateurÉtape 2 : Cliquez sur l'icône « Settings » (Paramètres) dans le menu vertical de droiteÉtape 3 : « Advanced settings » (Paramètres avancés) => « CSS in the section» (CSS dans la section )Étape 4 : Assurez-vous que l'option « Do not strip CSS from » (Ne pas supprimer le CSS de la section ) est cochéeÉtape 5 : Cliquez sur le bouton « SAVE WORK » (SAUVEGARDER LE TRAVAIL)Modifiez le style de tous les formulaires globalement en un seul clicModifiez le style de chaque formulaire individuellement en utilisant n'importe quel thèmeLe thème de style est désactivé pour tous les formulairesLe thème de style est désactivé pour ce formulaireLe thème de style est activé pour ce formulaireLe thème de style n'est pas choisiListe des thèmes de styleAperçu des thèmes de styleParamètres des thèmes de styleLe style sera chargé dans la baliseLe style sera chargé dans la balise Effectué avec succèsSupport & KBVue dans la tabletteCouleur du texteCe thème de style est activé globalement pour tous les formulaires. Si vous souhaitez utiliser un style de votre motif pour le formulaire Contact Form 7, cliquez sur le bouton « Désactiver ».Le style de ce formulaire est modifié globalement avec %s, vous pouvez le modifier avec le thème de style actuel.Le style de ce formulaire est modifié avec %s, vous pouvez le modifier avec le thème de style actuel.Le style de ce formulaire est modifié avec le Thème de style actuel, vous pouvez le désactiver et utiliser les paramètres globaux.Le style de ce formulaire n'est modifié par aucun thème de style, vous pouvez soit activer le thème actuel pour ce formulaire, soit définir le modèle de style global en cliquant sur « Utiliser pour tous les formulaires » sous le nom du thème.Ce thème n'existe pasTobias ConradTutorielStyle non modifiéMise à jour vers la version ProCharger l'imageLongueur verticaleWOW Style Contact Form 7BienvenuLargeurOUIVous pouvez créer un nouveau thème de style avec les paramètres du thème actuel. Si vous souhaitez créer un thème de style vide, décochez la case ci-dessous.Vous ne pouvez pas supprimerVous ne pouvez pas utiliser ces paramètres sur des pages comme celle-ciVous n'avez pas d'éléments de formulaire Contact Form 7Vous n'utilisez aucun thème de style pour le formulaire Contact Form 7. Cliquez sur « Utiliser pour tous les formulaires » pour utiliser la thème actuel de façon globale.suppriméeactivé pour tous les formulaireshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/languages/cf7-styler-es_MX.po000064400000110744147600046700012075 0ustar00msgid "" msgstr "" "Language: es-MX\n" "POT-Creation-Date: 2023-04-15 15:26+0000\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "PO-Revision-Date: 2023-04-15 16:10+0000\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: WOW Style Contact Form 7\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: Spanish (Mexico)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- desactivar el esquema de estilo -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "- seleccionar -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14 días de prueba" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s utilizado para todos los formularios globalmente, haga " "clic en \"Utilizar para todos los formularios\" si desea utilizar el Esquema " "actual." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Activar el estilo para todos los formularios*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Activar el estilo para el formulario actual" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "Como alternativa puedes desactivar limpiar los estilos como en Thrive " "architect:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Color de fondo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Imagen de fondo" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Antes de utilizar WOW Style Contact Form 7, necesita instalar y activar el " "plugin Contact Form 7." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "Color BG" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "Opacidad BG" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Radio de desenfoque" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "Frontera" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Color del borde" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Radio del borde" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Tipo de borde" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "Anchura del borde" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "Borde del botón" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Botones" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Por Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "Cancelar" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "CF7 Customizer requiere que el plugin Contact Form 7 esté instalado y activo." " Puede descargar %s." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "El formulario CF7 no está seleccionado" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7 Styler" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Casillas de verificación y botones de radio" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "Estilos limpios" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "Claro" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "Cerrar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Color" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Formulario de contacto 7 lista" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "Contiene" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Copiar la configuración actual del esquema de estilos" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "Portada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "Cree" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Crear varios esquemas de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Crear un nuevo esquema de estilo" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Estilo actual" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "Actualmente %s " "formulario tiene estilo con " "%s. Al igual que en la versión gratuita, sólo puede aplicar estilo " "a un formulario a la vez y si activa el estilo para el formulario actual, el " "estilo se eliminará del otro formulario." #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "Actualmente el formulario %s está estilizado con " "%s. Como en la versión gratuita puede estilo sólo una forma a la " "vez y si activa el estilo para la forma actual, estilo se eliminará de otra " "forma." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "CSS personalizado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "Código CSS personalizado" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Dashed" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Régimen por defecto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "Borrar imagen" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "suprimido" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Vista de escritorio" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Desactivar el estilo para todos los formularios" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Desactivar el estilo para el formulario actual" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "Te gusta conseguir styler versión premium gratis? ¡Entonces Introduzca su licencia " "WP2LEADS pro u obtenga una licencia aquí!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "¿Desea guardar los cambios antes de salir de la página?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "Puntos" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Doble" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Formulario duplicado en la segunda columna" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "activado para todos los formularios" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Error" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Salir a pantalla completa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Familia de fuentes" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "Tamaño de letra" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Estilo de fuente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "Peso de la fuente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Form BG Image & Colors" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Personalización de formularios" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Formulario no seleccionado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Relleno, margen y borde del formulario" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Texto del formulario" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "Pantalla completa" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "Ranura" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Longitud horizontal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Color BG" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "Color Hover" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Color del texto sobreimpresionado" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Cómo convertir su formulario de contacto form7 en un formulario de contacto " "de conversión, fácil de usar y con estilo profesional, un generador de " "prospectos \"encuesta\" o un formulario llamativo" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "https://saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "¡Estoy contento con esto! Guardar" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Si está utilizando constructores de páginas, como Thrive Architect, " "OptimizePress, etc., consulte nuestra página Base de conocimientos " "para solucionar posibles problemas" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Si necesita cargar esquema de estilo dentro de <body> " "etiqueta sólo en algunas páginas, puede hacerlo en frontend usando \"CF7 " "Styler\" botón. Esta función sólo está disponible para los tipos de mensajes " "individuales (páginas, mensajes, productos, etc) y en la versión premium. No " "se puede hacer en las páginas de archivos (blog, lista de productos, etc) en " "este caso es necesario utilizar la configuración global." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Si necesita cargar el esquema de estilo dentro de la etiqueta <" "body> sólo en algunas páginas, puede hacerlo en frontend usando el " "botón \"CF7 Styler\". Esta función sólo está disponible para los tipos de " "entradas individuales (páginas, entradas, productos, etc.). No se puede " "hacer en las páginas de archivos (blog, lista de productos, etc) en este " "caso es necesario utilizar la configuración global." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "La configuración del fondo de la imagen sólo está disponible en la versión " "Profesional. Puede probarlo en el modo de vista previa \"estilo actual\", " "pero no se guardará." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Opacidad de la imagen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Posición de la imagen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "Tamaño de la imagen" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Heredar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Campos de entrada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Título del esquema de estilo de entrada" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Insertar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Cursiva" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Etiquetas Color" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Etiquetas Tamaño de fuente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Etiquetas Ajustes" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Licencia" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "Altura de línea" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Enlaces Color" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Color de los enlaces" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Configuración de enlaces" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "En directo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Cargar estilos en la etiqueta <body><body> tag on this page" msgstr "" "Cargar estilos dentro de la etiqueta <body> en esta página" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Marque una casilla por línea" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "¿Hacer ancho completo?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "¿Completar los campos de entrada?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Hacer un elemento radiobutton por línea" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Margen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Vista móvil" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "NO" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "No hay elementos de Contact Form 7 para la vista previa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "Ningún formulario tiene un esquema de estilo, haga clic en el botón " "\"Activar estilo para formulario actual\" para aplicar el esquema actual a " "este formulario." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "No se ha seleccionado ningún plugin para instalar" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "No se ha seleccionado ningún esquema de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "Normal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Oblicuo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "Estilos uno por línea para casillas de verificación y radiobuttons en modo " "activo sólo para la versión Profesional. Puede probarlo en el modo de vista " "previa \"estilo actual\", pero no se guardará." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Opacidad" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Estilizador abierto" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "Ver cuenta" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "Tamaño original" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Esquema" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "Salida" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Acolchado" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Introduzca el título del esquema de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Vista previa" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "Modo de vista previa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Vista previa Unstyled" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Radio" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "Repita ambos" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "Repetir horizontal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "Repetir vertical" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Restablecer valores por defecto" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Cresta" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "Guardar" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "Guardado" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Vista de la segunda columna" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Seleccione el esquema de estilo para este formulario" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Ajustes" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "Ajustes guardados como" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "Sombra" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "Sombra Color" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Posición de sombra" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Sólido" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Algunos temas o constructores de página (fe. Thrive Architect, OptimizePress " "etc.) podrían eliminar estilos en línea dentro de la etiqueta <" "head>. Cargar el esquema de estilo dentro de la etiqueta <" "body> solucionará este problema y mostrará sus formularios con " "estilo." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "Modo dividido" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "Vista dividida" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Radio de dispersión" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Empezar a estilizar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Empezar creando un esquema de estilo por defecto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Comienza creando tu primer Formulario " "de Contacto 7" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "¡Comience su prueba gratuita de 14 días con todas las funciones " "profesionales aquí!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Paso 1. Haga clic en el enlace \"Editar con Thrive architect\" en la lista " "de páginas de administración" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "" "Paso 2. Haga clic en el icono \"Configuración\" del menú vertical derecho" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" "Paso 3. \"Configuración avanzada\" => \"CSS en la sección <head>\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "Paso 4. Asegúrese de que \"No quitar CSS de <head>\" está marcada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Paso 5. Pulse el botón \"GUARDAR TRABAJO" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "Estiliza todos los formularios globalmente con un solo clic" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "" "Estiliza cada formulario individualmente con cualquier esquema de estilo" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Esquema de estilo desactivado para todos los formularios" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Esquema de estilo desactivado para este formulario" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Esquema de estilo activado para este formulario" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "El esquema de estilo no está seleccionado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "Lista de esquemas de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Vista previa de los esquemas de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "Ajustes de los esquemas de estilo" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "El estilo se cargará en la etiqueta tag" msgstr "El estilo se cargará en la etiqueta " #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Éxito" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "Soporte y KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Vista de tableta" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Color del texto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Este formulario está estilizado con %s globalmente, puedes " "estilizarlo con el esquema de Estilo actual." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Este formulario está estilizado con %s, puedes estilizarlo " "con el esquema de Estilo actual." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Este formulario está diseñado con el esquema de estilo actual, puede " "desactivarlo y utilizar la configuración global." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Este formulario no tiene ningún esquema de estilo, puede activar el esquema " "actual para este formulario o configurar un esquema de estilo global " "haciendo clic en \"Usar para todos los formularios\" debajo del título del " "esquema." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Este régimen no existe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Este esquema de estilo habilitado a nivel mundial para todas las formas. Si " "desea utilizar el estilo de su tema para Contact Form 7 haga clic en el " "botón \"Desactivar\"." #. Author of the plugin msgid "Tobias Conrad" msgstr "Tobias Conrad" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "Tutorial" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Sin estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "Actualizar a Pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Cargar imagen" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "Longitud vertical" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Bienvenido" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "Anchura" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "Formulario de contacto WOW Style 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "SÍ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "Puede crear un nuevo esquema de estilo con la configuración actual. Si desea " "crear un esquema de estilo en blanco, desmarque la casilla de abajo." #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "No se puede borrar" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "No puede utilizar esta configuración en este tipo de página" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "No tiene ningún elemento Contact Form 7" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "No utiliza ninguno de los esquemas de estilo para Contact Form 7. Haga clic " "en \"Usar para todos los formularios\" para utilizar el esquema actual de " "forma global." languages/cf7-styler-es_MX.mo000064400000046364147600046700012100 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'(#))*"*-*+*P+k+ t+++a+,,,., >,L,^,o,w,b,', -"-,+-X-h-n-u-{--6---- . (.P6./r0000 00/0. 1;19Y222*222223 323I3&i3333333 3!4$4"4 567e9{999T:\:(n:::::::: ;;#; =;6H;N;;;"<('<P< W<d<7g<<2?=/r====z>> >>>> >+> >??0? 6?C?V?g?????4???@ @#@7@7?@ wAAAA0AZAIBgcCICFDG\D(D;DH E8RE2E/E*EF&5F!\F*~F+FF FFF GyGm-HxHII JJ (J3J DJRJ"dJ JJJJ1K=DK(KK ML#WL{L`L- disable style scheme -- select -14-days Trial%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Language: es-MX POT-Creation-Date: 2023-04-15 15:26+0000 Plural-Forms: nplurals=2; plural=n != 1; PO-Revision-Date: 2023-04-15 16:10+0000 X-Generator: Loco https://localise.biz/ Project-Id-Version: WOW Style Contact Form 7 Report-Msgid-Bugs-To: Last-Translator: Tobias support@saleswonder.biz Language-Team: Spanish (Mexico) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- desactivar el esquema de estilo -- seleccionar -14 días de prueba%s utilizado para todos los formularios globalmente, haga clic en "Utilizar para todos los formularios" si desea utilizar el Esquema actual.Activar el estilo para todos los formularios*Activar el estilo para el formulario actualComo alternativa puedes desactivar limpiar los estilos como en Thrive architect:Color BGOpacidad BGColor de fondoImagen de fondoAntes de utilizar WOW Style Contact Form 7, necesita instalar y activar el plugin Contact Form 7.Radio de desenfoqueFronteraColor del bordeRadio del bordeTipo de bordeAnchura del bordeBorde del botónBotonesPor Takayuki MiyoshiCF7 Customizer requiere que el plugin Contact Form 7 esté instalado y activo. Puede descargar %s.El formulario CF7 no está seleccionadoCF7 StylerCancelarCasillas de verificación y botones de radioEstilos limpiosClaroCerrarColorFormulario de contacto 7 listaContieneCopiar la configuración actual del esquema de estilosPortadaCreeCrear varios esquemas de estiloCrear un nuevo esquema de estiloEstilo actualActualmente %s formulario tiene estilo con %s. Al igual que en la versión gratuita, sólo puede aplicar estilo a un formulario a la vez y si activa el estilo para el formulario actual, el estilo se eliminará del otro formulario.Actualmente el formulario %s está estilizado con %s. Como en la versión gratuita puede estilo sólo una forma a la vez y si activa el estilo para la forma actual, estilo se eliminará de otra forma.CSS personalizadoCódigo CSS personalizadoDashedRégimen por defectoBorrar imagenVista de escritorioDesactivar el estilo para todos los formulariosDesactivar el estilo para el formulario actualTe gusta conseguir styler versión premium gratis? ¡Entonces Introduzca su licencia WP2LEADS pro u obtenga una licencia aquí!¿Desea guardar los cambios antes de salir de la página?PuntosDobleFormulario duplicado en la segunda columnaErrorSalir a pantalla completaFamilia de fuentesTamaño de letraEstilo de fuentePeso de la fuenteForm BG Image & ColorsPersonalización de formulariosRelleno, margen y borde del formularioTexto del formularioFormulario no seleccionadoPantalla completaRanuraLongitud horizontalColor BGColor HoverColor del texto sobreimpresionadoCómo convertir su formulario de contacto form7 en un formulario de contacto de conversión, fácil de usar y con estilo profesional, un generador de prospectos "encuesta" o un formulario llamativo¡Estoy contento con esto! GuardarSi está utilizando constructores de páginas, como Thrive Architect, OptimizePress, etc., consulte nuestra página Base de conocimientos para solucionar posibles problemasSi necesita cargar esquema de estilo dentro de <body> etiqueta sólo en algunas páginas, puede hacerlo en frontend usando "CF7 Styler" botón. Esta función sólo está disponible para los tipos de mensajes individuales (páginas, mensajes, productos, etc) y en la versión premium. No se puede hacer en las páginas de archivos (blog, lista de productos, etc) en este caso es necesario utilizar la configuración global.Si necesita cargar el esquema de estilo dentro de la etiqueta <body> sólo en algunas páginas, puede hacerlo en frontend usando el botón "CF7 Styler". Esta función sólo está disponible para los tipos de entradas individuales (páginas, entradas, productos, etc.). No se puede hacer en las páginas de archivos (blog, lista de productos, etc) en este caso es necesario utilizar la configuración global.Opacidad de la imagenPosición de la imagenTamaño de la imagenLa configuración del fondo de la imagen sólo está disponible en la versión Profesional. Puede probarlo en el modo de vista previa "estilo actual", pero no se guardará.HeredarCampos de entradaTítulo del esquema de estilo de entradaInsertarCursivaEtiquetas ColorEtiquetas Tamaño de fuenteEtiquetas AjustesLicenciaAltura de líneaEnlaces ColorColor de los enlacesConfiguración de enlacesEn directoCargar estilos en la etiqueta <body><body> en esta páginaMarque una casilla por línea¿Hacer ancho completo?¿Completar los campos de entrada?Hacer un elemento radiobutton por líneaMargenVista móvilNONo hay elementos de Contact Form 7 para la vista previaNingún formulario tiene un esquema de estilo, haga clic en el botón "Activar estilo para formulario actual" para aplicar el esquema actual a este formulario.No se ha seleccionado ningún plugin para instalarNo se ha seleccionado ningún esquema de estiloNormalOblicuoEstilos uno por línea para casillas de verificación y radiobuttons en modo activo sólo para la versión Profesional. Puede probarlo en el modo de vista previa "estilo actual", pero no se guardará.OpacidadEstilizador abiertoVer cuentaTamaño originalEsquemaSalidaAcolchadoIntroduzca el título del esquema de estiloVista previaVista previa UnstyledModo de vista previaRadioRepita ambosRepetir horizontalRepetir verticalRestablecer valores por defectoCrestaGuardarGuardadoVista de la segunda columnaSeleccione el esquema de estilo para este formularioAjustesAjustes guardados comoSombraSombra ColorPosición de sombraSólidoAlgunos temas o constructores de página (fe. Thrive Architect, OptimizePress etc.) podrían eliminar estilos en línea dentro de la etiqueta <head>. Cargar el esquema de estilo dentro de la etiqueta <body> solucionará este problema y mostrará sus formularios con estilo.Modo divididoVista divididaRadio de dispersiónEmpezar a estilizarEmpezar creando un esquema de estilo por defectoComienza creando tu primer Formulario de Contacto 7¡Comience su prueba gratuita de 14 días con todas las funciones profesionales aquí!Paso 1. Haga clic en el enlace "Editar con Thrive architect" en la lista de páginas de administraciónPaso 2. Haga clic en el icono "Configuración" del menú vertical derechoPaso 3. "Configuración avanzada" => "CSS en la sección <head>"Paso 4. Asegúrese de que "No quitar CSS de <head>" está marcadaPaso 5. Pulse el botón "GUARDAR TRABAJOEstiliza todos los formularios globalmente con un solo clicEstiliza cada formulario individualmente con cualquier esquema de estiloEsquema de estilo desactivado para todos los formulariosEsquema de estilo desactivado para este formularioEsquema de estilo activado para este formularioEl esquema de estilo no está seleccionadoLista de esquemas de estiloVista previa de los esquemas de estiloAjustes de los esquemas de estiloEl estilo se cargará en la etiqueta ÉxitoSoporte y KBVista de tabletaColor del textoEste esquema de estilo habilitado a nivel mundial para todas las formas. Si desea utilizar el estilo de su tema para Contact Form 7 haga clic en el botón "Desactivar".Este formulario está estilizado con %s globalmente, puedes estilizarlo con el esquema de Estilo actual.Este formulario está estilizado con %s, puedes estilizarlo con el esquema de Estilo actual.Este formulario está diseñado con el esquema de estilo actual, puede desactivarlo y utilizar la configuración global.Este formulario no tiene ningún esquema de estilo, puede activar el esquema actual para este formulario o configurar un esquema de estilo global haciendo clic en "Usar para todos los formularios" debajo del título del esquema.Este régimen no existeTobias ConradTutorialSin estiloActualizar a ProCargar imagenLongitud verticalFormulario de contacto WOW Style 7BienvenidoAnchuraSÍPuede crear un nuevo esquema de estilo con la configuración actual. Si desea crear un esquema de estilo en blanco, desmarque la casilla de abajo.No se puede borrarNo puede utilizar esta configuración en este tipo de páginaNo tiene ningún elemento Contact Form 7No utiliza ninguno de los esquemas de estilo para Contact Form 7. Haga clic en "Usar para todos los formularios" para utilizar el esquema actual de forma global.suprimidoactivado para todos los formularioshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/languages/cf7-styler-es_ES.po000064400000113146147600046700012057 0ustar00msgid "" msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Loco https://localise.biz/\n" "Project-Id-Version: Wow style (ES)\n" "Language: es-ES\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "POT-Creation-Date: 2020-04-18 12:29+0000\n" "PO-Revision-Date: 2023-04-15 16:05+0000\n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: Spanish (Spain)\n" "Report-Msgid-Bugs-To: \n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- desactivar el esquema de estilo -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "- seleccionar -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14 días de prueba" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #| msgid "" #| "%s used for all forms globally, click \"Use for all forms\" if you want to " #| "use current Scheme." msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s utilizado para todos los formularios globalmente, haga " "clic en \"Utilizar para todos los formularios\" si desea utilizar el Esquema " "actual." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Activar el estilo para todos los formularios*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Activar el estilo para el formulario actual" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "Como alternativa puedes desactivar limpiar los estilos como en Thrive " "architect:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Color de fondo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Imagen de fondo" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Antes de utilizar WOW Style Contact Form 7, necesita instalar y activar el " "plugin Contact Form 7." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "Color BG" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "Opacidad BG" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Radio de desenfoque" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "Frontera" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Color del borde" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Radio del borde" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Tipo de borde" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "Anchura del borde" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "Borde del botón" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Botones" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Por Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "Cancelar" #: cf7-styler.php:129 msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "CF7 Customizer requiere que el plugin Contact Form 7 esté instalado y activo." " Puede descargar %s." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "El formulario CF7 no está seleccionado" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7 Styler" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Casillas de verificación y botones de radio" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "Estilos limpios" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "Claro" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "Cerrar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Color" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Formulario de contacto 7 lista" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "Contiene" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Copiar la configuración actual del esquema de estilos" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "Portada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "Cree" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Crear varios esquemas de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Crear un nuevo esquema de estilo" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Estilo actual" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "Actualmente %s " "formulario tiene estilo con " "%s. Al igual que en la versión gratuita, sólo puede aplicar estilo " "a un formulario a la vez y si activa el estilo para el formulario actual, el " "estilo se eliminará del otro formulario." #: admin/class-cf7-customizer-admin.php:481 #| msgid "" #| "Currently %s form is styled with %s. As in free version you can style only " #| "one form at a time and if you activate style for current form, style will be " #| "removed from other form." msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "Actualmente el formulario %s está estilizado con " "%s. Como en la versión gratuita puede estilo sólo una forma a la " "vez y si activa el estilo para la forma actual, estilo se eliminará de otra " "forma." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "CSS personalizado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "Código CSS personalizado" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Dashed" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Régimen por defecto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "Borrar imagen" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "suprimido" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Vista de escritorio" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Desactivar el estilo para todos los formularios" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Desactivar el estilo para el formulario actual" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "Te gusta conseguir styler versión premium gratis? ¡Entonces Introduzca su licencia " "WP2LEADS pro u obtenga una licencia aquí!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "¿Desea guardar los cambios antes de salir de la página?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "Puntos" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Doble" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Formulario duplicado en la segunda columna" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "activado para todos los formularios" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Error" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Salir a pantalla completa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Familia de fuentes" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "Tamaño de letra" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Estilo de fuente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "Peso de la fuente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Form BG Image & Colors" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Personalización de formularios" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Formulario no seleccionado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Relleno, margen y borde del formulario" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Texto del formulario" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "Pantalla completa" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "Ranura" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Longitud horizontal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Color BG" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "Color Hover" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Color del texto sobreimpresionado" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Cómo convertir su formulario de contacto form7 en un formulario de contacto " "de conversión, fácil de usar y con estilo profesional, un generador de " "prospectos \"encuesta\" o un formulario llamativo" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "https://saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "¡Estoy contento con esto! Guardar" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "Si está utilizando constructores de páginas, como Thrive Architect, " "OptimizePress, etc., consulte nuestra página Base de conocimientos " "para solucionar posibles problemas" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Si necesita cargar esquema de estilo dentro de <body> " "etiqueta sólo en algunas páginas, puede hacerlo en frontend usando \"CF7 " "Styler\" botón. Esta función sólo está disponible para los tipos de mensajes " "individuales (páginas, mensajes, productos, etc) y en la versión premium. No " "se puede hacer en las páginas de archivos (blog, lista de productos, etc) en " "este caso es necesario utilizar la configuración global." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Si necesita cargar el esquema de estilo dentro de la etiqueta <" "body> sólo en algunas páginas, puede hacerlo en frontend usando el " "botón \"CF7 Styler\". Esta función sólo está disponible para los tipos de " "entradas individuales (páginas, entradas, productos, etc.). No se puede " "hacer en las páginas de archivos (blog, lista de productos, etc) en este " "caso es necesario utilizar la configuración global." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "La configuración del fondo de la imagen sólo está disponible en la versión " "Profesional. Puede probarlo en el modo de vista previa \"estilo actual\", " "pero no se guardará." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Opacidad de la imagen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Posición de la imagen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "Tamaño de la imagen" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Heredar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Campos de entrada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Título del esquema de estilo de entrada" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Insertar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Cursiva" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Etiquetas Color" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Etiquetas Tamaño de fuente" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Etiquetas Ajustes" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Licencia" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "Altura de línea" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Enlaces Color" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Color de los enlaces" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Configuración de enlaces" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "En directo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Cargar estilos en la etiqueta <body><body> tag on this page" msgstr "" "Cargar estilos dentro de la etiqueta <body> en esta página" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Marque una casilla por línea" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "¿Hacer ancho completo?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "¿Completar los campos de entrada?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Hacer un elemento radiobutton por línea" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Margen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Vista móvil" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "NO" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "No hay elementos de Contact Form 7 para la vista previa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "Ningún formulario tiene un esquema de estilo, haga clic en el botón " "\"Activar estilo para formulario actual\" para aplicar el esquema actual a " "este formulario." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "No se ha seleccionado ningún plugin para instalar" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "No se ha seleccionado ningún esquema de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "Normal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Oblicuo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "Estilos uno por línea para casillas de verificación y radiobuttons en modo " "actual sólo para la versión Profesional. Puede probarlo en el modo de vista " "previa \"estilo actual\", pero no se guardará." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Opacidad" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Estilizador abierto" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "Ver cuenta" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "Tamaño original" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Esquema" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "Salida" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Acolchado" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Introduzca el título del esquema de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Vista previa" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "Modo de vista previa" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Vista previa Unstyled" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Radio" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "Repita ambos" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "Repetir horizontal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "Repetir vertical" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Restablecer valores por defecto" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Cresta" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "Guardar" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "Guardado" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Vista de la segunda columna" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Seleccione el esquema de estilo para este formulario" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Ajustes" #: admin/class-cf7-customizer-admin-ajax.php:210 #| msgid "Settings saved as" msgid "Settings saved as " msgstr "Ajustes guardados como" #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "Sombra" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "Sombra Color" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Posición de sombra" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Sólido" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 #| msgid "" #| "Some themes or page builders (e.g. Thrive Architect, OptimizePress etc.) " #| "could remove inline styles inside tag. Loading style scheme within " #| " tag will fix this issue and show your forms styled." msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Algunos temas o constructores de página (fe. Thrive Architect, OptimizePress " "etc.) podrían eliminar estilos en línea dentro de la etiqueta <" "head>. Cargar el esquema de estilo dentro de la etiqueta <" "body> solucionará este problema y mostrará sus formularios con " "estilo." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "Modo dividido" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "Vista dividida" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Radio de dispersión" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Empezar a estilizar" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Empezar creando un esquema de estilo por defecto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Comienza creando tu primer Formulario " "de Contacto 7" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "¡Comience su prueba gratuita de 14 días con todas las funciones " "profesionales aquí!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Paso 1. Haga clic en el enlace \"Editar con Thrive architect\" en la lista " "de páginas de administración" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "" "Paso 2. Haga clic en el icono \"Configuración\" del menú vertical derecho" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 #| msgid "Step 3. \"Advanced settings\" => \"CSS in the section\"" msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" "Paso 3. \"Configuración avanzada\" => \"CSS en la sección <head>\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 #| msgid "Step 4. Make sure that \"Do not strip CSS from \" is checked" msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "Paso 4. Asegúrese de que \"No quitar CSS de <head>\" está marcada" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Paso 5. Pulse el botón \"GUARDAR TRABAJO" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "Estiliza todos los formularios globalmente con un solo clic" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "" "Estiliza cada formulario individualmente con cualquier esquema de estilo" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Esquema de estilo desactivado para todos los formularios" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Esquema de estilo desactivado para este formulario" #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Esquema de estilo habilitado para este formulario" #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "El esquema de estilo no está seleccionado" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "Lista de esquemas de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Vista previa de los esquemas de estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "Ajustes de los esquemas de estilo" #: admin/class-cf7-customizer-admin-ajax.php:298 #| msgid "Style will be loaded in tag" msgid "Style will be loaded in tag" msgstr "El estilo se cargará en la etiqueta tag" msgstr "El estilo se cargará en la etiqueta " #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Éxito" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "Soporte y KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Vista de tableta" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Color del texto" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #| msgid "" #| "This form is styled with %s globally, you can style it with current Style " #| "scheme." msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Este formulario está estilizado con %s globalmente, puedes " "estilizarlo con el esquema de Estilo actual." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #| msgid "" #| "This form is styled with %s, you can style it with current Style scheme." msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Este formulario está estilizado con %s, puedes estilizarlo " "con el esquema de Estilo actual." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Este formulario está diseñado con el esquema de estilo actual, puede " "desactivarlo y utilizar la configuración global." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Este formulario no tiene ningún esquema de estilo, puede activar el esquema " "actual para este formulario o configurar un esquema de estilo global " "haciendo clic en \"Usar para todos los formularios\" debajo del título del " "esquema." #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Este régimen no existe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Este esquema de estilo habilitado a nivel mundial para todas las formas. Si " "desea utilizar el estilo de su tema para Contact Form 7 haga clic en el " "botón \"Desactivar\"." #. Author of the plugin msgid "Tobias Conrad" msgstr "Tobias Conrad" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "Tutorial" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Sin estilo" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "Actualizar a Pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Cargar imagen" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "Longitud vertical" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Bienvenido" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "Anchura" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "Formulario de contacto WOW Style 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "SÍ" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 #| msgid "" #| "You can create new style scheme with current scheme settings. If you want to " #| "create blank style scheme uncheck checkbox below." msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "Puede crear un nuevo esquema de estilo con la configuración actual. Si desea " "crear un esquema de estilo en blanco, desmarque la casilla de abajo." #: admin/class-cf7-customizer-admin-ajax.php:237 #| msgid "You cannot delete" msgid "You can not delete" msgstr "No se puede borrar" #: public/class-cf7-customizer-public.php:471 #| msgid "You cannot use this settings on this page type" msgid "You can not use this settings on this page type" msgstr "No puede utilizar esta configuración en este tipo de página" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "No tiene ningún elemento Contact Form 7" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "No utiliza ninguno de los esquemas de estilo para Contact Form 7. Haga clic " "en \"Usar para todos los formularios\" para utilizar el esquema actual de " "forma global." languages/cf7-styler-es_ES.mo000064400000046355147600046700012063 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'(#))**-*+*P+b+ k+w++a++ ,,%, 5,C,U,f,n,b,', --,"-O-_-e-l-r--6---- - .P-.~/i0{000 00/0.1219P222*2222233)3@3&`3333333 3!34"4557\9r999K:S:(e:::::::: :;; 4;6?;Nv;;;";(<G< N<[<7^<<26=/i====q>z> >>>> >+> >>?'? -?:?M?^?~????4???@ @@.@76@ nA|AAA0AZA@BgZCICF DGSD(D;DHE8IE2E1E*EF&.F!UF*wF+FF FFFGyGm&HxH II JJ !J,J =JKJ"]J JJJJ*K==K({KK FL#PLtL`L- disable style scheme -- select -14-days Trial%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Loco https://localise.biz/ Project-Id-Version: Wow style (ES) Language: es-ES Plural-Forms: nplurals=2; plural=(n != 1); POT-Creation-Date: 2020-04-18 12:29+0000 PO-Revision-Date: 2023-04-15 16:05+0000 Last-Translator: Tobias support@saleswonder.biz Language-Team: Spanish (Spain) Report-Msgid-Bugs-To: X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- desactivar el esquema de estilo -- seleccionar -14 días de prueba%s utilizado para todos los formularios globalmente, haga clic en "Utilizar para todos los formularios" si desea utilizar el Esquema actual.Activar el estilo para todos los formularios*Activar el estilo para el formulario actualComo alternativa puedes desactivar limpiar los estilos como en Thrive architect:Color BGOpacidad BGColor de fondoImagen de fondoAntes de utilizar WOW Style Contact Form 7, necesita instalar y activar el plugin Contact Form 7.Radio de desenfoqueFronteraColor del bordeRadio del bordeTipo de bordeAnchura del bordeBorde del botónBotonesPor Takayuki MiyoshiCF7 Customizer requiere que el plugin Contact Form 7 esté instalado y activo. Puede descargar %s.El formulario CF7 no está seleccionadoCF7 StylerCancelarCasillas de verificación y botones de radioEstilos limpiosClaroCerrarColorFormulario de contacto 7 listaContieneCopiar la configuración actual del esquema de estilosPortadaCreeCrear varios esquemas de estiloCrear un nuevo esquema de estiloEstilo actualActualmente %s formulario tiene estilo con %s. Al igual que en la versión gratuita, sólo puede aplicar estilo a un formulario a la vez y si activa el estilo para el formulario actual, el estilo se eliminará del otro formulario.Actualmente el formulario %s está estilizado con %s. Como en la versión gratuita puede estilo sólo una forma a la vez y si activa el estilo para la forma actual, estilo se eliminará de otra forma.CSS personalizadoCódigo CSS personalizadoDashedRégimen por defectoBorrar imagenVista de escritorioDesactivar el estilo para todos los formulariosDesactivar el estilo para el formulario actualTe gusta conseguir styler versión premium gratis? ¡Entonces Introduzca su licencia WP2LEADS pro u obtenga una licencia aquí!¿Desea guardar los cambios antes de salir de la página?PuntosDobleFormulario duplicado en la segunda columnaErrorSalir a pantalla completaFamilia de fuentesTamaño de letraEstilo de fuentePeso de la fuenteForm BG Image & ColorsPersonalización de formulariosRelleno, margen y borde del formularioTexto del formularioFormulario no seleccionadoPantalla completaRanuraLongitud horizontalColor BGColor HoverColor del texto sobreimpresionadoCómo convertir su formulario de contacto form7 en un formulario de contacto de conversión, fácil de usar y con estilo profesional, un generador de prospectos "encuesta" o un formulario llamativo¡Estoy contento con esto! GuardarSi está utilizando constructores de páginas, como Thrive Architect, OptimizePress, etc., consulte nuestra página Base de conocimientos para solucionar posibles problemasSi necesita cargar esquema de estilo dentro de <body> etiqueta sólo en algunas páginas, puede hacerlo en frontend usando "CF7 Styler" botón. Esta función sólo está disponible para los tipos de mensajes individuales (páginas, mensajes, productos, etc) y en la versión premium. No se puede hacer en las páginas de archivos (blog, lista de productos, etc) en este caso es necesario utilizar la configuración global.Si necesita cargar el esquema de estilo dentro de la etiqueta <body> sólo en algunas páginas, puede hacerlo en frontend usando el botón "CF7 Styler". Esta función sólo está disponible para los tipos de entradas individuales (páginas, entradas, productos, etc.). No se puede hacer en las páginas de archivos (blog, lista de productos, etc) en este caso es necesario utilizar la configuración global.Opacidad de la imagenPosición de la imagenTamaño de la imagenLa configuración del fondo de la imagen sólo está disponible en la versión Profesional. Puede probarlo en el modo de vista previa "estilo actual", pero no se guardará.HeredarCampos de entradaTítulo del esquema de estilo de entradaInsertarCursivaEtiquetas ColorEtiquetas Tamaño de fuenteEtiquetas AjustesLicenciaAltura de líneaEnlaces ColorColor de los enlacesConfiguración de enlacesEn directoCargar estilos en la etiqueta <body><body> en esta páginaMarque una casilla por línea¿Hacer ancho completo?¿Completar los campos de entrada?Hacer un elemento radiobutton por líneaMargenVista móvilNONo hay elementos de Contact Form 7 para la vista previaNingún formulario tiene un esquema de estilo, haga clic en el botón "Activar estilo para formulario actual" para aplicar el esquema actual a este formulario.No se ha seleccionado ningún plugin para instalarNo se ha seleccionado ningún esquema de estiloNormalOblicuoEstilos uno por línea para casillas de verificación y radiobuttons en modo actual sólo para la versión Profesional. Puede probarlo en el modo de vista previa "estilo actual", pero no se guardará.OpacidadEstilizador abiertoVer cuentaTamaño originalEsquemaSalidaAcolchadoIntroduzca el título del esquema de estiloVista previaVista previa UnstyledModo de vista previaRadioRepita ambosRepetir horizontalRepetir verticalRestablecer valores por defectoCrestaGuardarGuardadoVista de la segunda columnaSeleccione el esquema de estilo para este formularioAjustesAjustes guardados comoSombraSombra ColorPosición de sombraSólidoAlgunos temas o constructores de página (fe. Thrive Architect, OptimizePress etc.) podrían eliminar estilos en línea dentro de la etiqueta <head>. Cargar el esquema de estilo dentro de la etiqueta <body> solucionará este problema y mostrará sus formularios con estilo.Modo divididoVista divididaRadio de dispersiónEmpezar a estilizarEmpezar creando un esquema de estilo por defectoComienza creando tu primer Formulario de Contacto 7¡Comience su prueba gratuita de 14 días con todas las funciones profesionales aquí!Paso 1. Haga clic en el enlace "Editar con Thrive architect" en la lista de páginas de administraciónPaso 2. Haga clic en el icono "Configuración" del menú vertical derechoPaso 3. "Configuración avanzada" => "CSS en la sección <head>"Paso 4. Asegúrese de que "No quitar CSS de <head>" está marcadaPaso 5. Pulse el botón "GUARDAR TRABAJOEstiliza todos los formularios globalmente con un solo clicEstiliza cada formulario individualmente con cualquier esquema de estiloEsquema de estilo desactivado para todos los formulariosEsquema de estilo desactivado para este formularioEsquema de estilo habilitado para este formularioEl esquema de estilo no está seleccionadoLista de esquemas de estiloVista previa de los esquemas de estiloAjustes de los esquemas de estiloEl estilo se cargará en la etiqueta ÉxitoSoporte y KBVista de tabletaColor del textoEste esquema de estilo habilitado a nivel mundial para todas las formas. Si desea utilizar el estilo de su tema para Contact Form 7 haga clic en el botón "Desactivar".Este formulario está estilizado con %s globalmente, puedes estilizarlo con el esquema de Estilo actual.Este formulario está estilizado con %s, puedes estilizarlo con el esquema de Estilo actual.Este formulario está diseñado con el esquema de estilo actual, puede desactivarlo y utilizar la configuración global.Este formulario no tiene ningún esquema de estilo, puede activar el esquema actual para este formulario o configurar un esquema de estilo global haciendo clic en "Usar para todos los formularios" debajo del título del esquema.Este régimen no existeTobias ConradTutorialSin estiloActualizar a ProCargar imagenLongitud verticalFormulario de contacto WOW Style 7BienvenidoAnchuraSÍPuede crear un nuevo esquema de estilo con la configuración actual. Si desea crear un esquema de estilo en blanco, desmarque la casilla de abajo.No se puede borrarNo puede utilizar esta configuración en este tipo de páginaNo tiene ningún elemento Contact Form 7No utiliza ninguno de los esquemas de estilo para Contact Form 7. Haga clic en "Usar para todos los formularios" para utilizar el esquema actual de forma global.suprimidoactivado para todos los formularioshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/languages/cf7-styler-de_DE.po000064400000111154147600046700012016 0ustar00msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-02-06 11:35+0000\n" "PO-Revision-Date: 2023-04-15 16:02+0000\n" "Last-Translator: Tobias support@saleswonder.biz\n" "Language-Team: German\n" "Language: de-DE\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Loco https://localise.biz/\n" "X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489" #: admin/class-cf7-customizer-admin.php:463 msgid "- disable style scheme -" msgstr "- Style deaktivieren -" #: includes/lib/Cf7_Template.php:68 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:463 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:517 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:672 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:789 msgid "- select -" msgstr "- wähle -" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 msgid "14-days Trial" msgstr "14 Tage testen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:231 #, php-format msgid "" "%s used for all forms globally, click \"Use for all forms\" " "if you want to use current Scheme." msgstr "" "%s-Style ist derzeit für alle Formulare aktiviert. Klicke " "auf den Button \"Aktiviere den Style für alle Formulare\" um den " "ausgewählten Style für alle Formulare zu nutzen. *Einzeln gestylte Formulare " "behalten ihren Style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:242 msgid "Activate style for all forms*" msgstr "Aktiviere den Style für alle Formulare*" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1292 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1329 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1396 msgid "Activate style for current form" msgstr "Aktiviere den Style für das ausgewählte Formular" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:318 msgid "" "As an alternative you can disable clean out the styles like on Thrive " "architect:" msgstr "" "Alternative kannst du das Entfernen der Styles deaktivieren wie im Beispiel " "Thrive Architect:" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:532 msgid "Background Color" msgstr "Hintergrundfarbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:560 msgid "Background Image" msgstr "Hintergrundbild" #: admin/partials/cf7-customizer-admin-tab-required-plugin.php:9 msgid "" "Before using WOW Style Contact Form 7, you need to install and activate " "Contact Form 7 plugin." msgstr "" "Bevor mit WOW-Stil Contact Form 7, müssen Sie installieren und aktivieren " "Contact Form 7 plugin." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:853 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1076 msgid "BG Color" msgstr "Hintergrund-Farbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:859 msgid "BG Opacity" msgstr "Hintergrund-Deckkraft" #: includes/lib/Cf7_Template.php:43 msgid "Blur radius" msgstr "Weichzeichner-Radius" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:943 msgid "Border" msgstr "Rahmen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:807 msgid "Border Color" msgstr "Rahmenfarbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:802 msgid "Border Radius" msgstr "Rahmen-Radius" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:786 msgid "Border Type" msgstr "Rahmen-Style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:764 msgid "Border Width" msgstr "Rahmenbreite" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1102 msgid "Button Border" msgstr "Button-Rahmen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1041 msgid "Buttons" msgstr "Button(s)" #: includes/lib/Cf7_Required_Plugin.php:13 msgid "By Takayuki Miyoshi" msgstr "Von Takayuki Miyoshi" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:414 msgid "Cancel" msgstr "Abbrechen" #: cf7-styler.php:129 #, php-format msgid "" "CF7 Customizer requires Contact Form 7 plugin to be installed and active. " "You can download %s." msgstr "" "Contact Form 7 Styler erfordert ein installiertes und aktiviertes Contact " "Form 7-Plugin. Du kannst es hier herunterladen %s." #: admin/class-cf7-customizer-admin-ajax.php:487 msgid "CF7 Form is not selected" msgstr "Bitte wähle ein CF7-Formular" #: public/class-cf7-customizer-public.php:342 #: public/class-cf7-customizer-public.php:371 #: admin/class-cf7-customizer-admin.php:94 #: admin/class-cf7-customizer-admin.php:95 #: admin/class-cf7-customizer-admin.php:115 #: admin/class-cf7-customizer-admin.php:116 #: admin/class-cf7-customizer-admin.php:430 msgid "CF7 Styler" msgstr "CF7 Styler" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:986 msgid "Checkboxes & Radiobuttons" msgstr "Checkboxen & Radiobuttons" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:378 msgid "Clean Styles" msgstr "Auf Default-Style zurücksetzen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1144 msgid "Clear" msgstr "Leeren" #: public/class-cf7-customizer-public.php:360 msgid "Close" msgstr "Schließen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:973 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1116 msgid "Color" msgstr "Farbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1187 msgid "Contact form 7 list" msgstr "Contact Form 7-Auswahl" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:675 msgid "Contain" msgstr "enthalten" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:410 msgid "Copy current style scheme settings" msgstr "Kopiere die aktuellen Style-Einstellungen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:674 msgid "Cover" msgstr "gefüllt" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:413 msgid "Create" msgstr "Erstellen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:293 msgid "Create multiple style schemes" msgstr "Erstelle mehrere Style-Vorlagen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:370 msgid "Create new style scheme!" msgstr "Neuen Style erstellen!" #: admin/partials/cf7-customizer-admin-preview-mode.php:6 msgid "Current Style" msgstr "Vorschau" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1381 #, php-format msgid "" "Currently %s form is " "styled with %s. As " "in free version you can style only one form at a time and if you activate " "style for current form, style will be removed from other form." msgstr "" "Im Moment ist das Formular " "%s ist mit Style " "%s gestyled. Nachdem du in der Free Version nur ein Formular " "zurzeit stylen kannst, wird bei Auswahl eines anderen Formulars das zuletzt " "gestylte Formular ungestyled." #: admin/class-cf7-customizer-admin.php:481 #, php-format msgid "" "Currently %s form is styled with %s. As in " "free version you can style only one form at a time and if you activate style " "for current form, style will be removed from other form." msgstr "" "Im Moment ist das Formular %s ist mit Style " "%s gestyled. Nachdem du in der Free Version nur ein Formular " "zurzeit stylen kannst, wird bei Auswahl eines anderen Formulars das zuletzt " "gestylte Formular ungestyled." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1134 msgid "Custom CSS" msgstr "Benutzerdefiniertes CSS" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1136 msgid "Custom CSS Code" msgstr "Benutzerdefinierter CSS-Code" #: includes/lib/Cf7_Template.php:80 msgid "Dashed" msgstr "Gestrichelte" #: admin/class-cf7-customizer-admin-ajax.php:66 #: admin/class-cf7-customizer-admin-ajax.php:237 #: includes/lib/Cf7_Style_Scheme.php:7 msgid "Default Scheme" msgstr "Standard-Style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:580 msgid "Delete Image" msgstr "Bild löschen" #: admin/class-cf7-customizer-admin-ajax.php:286 msgid "deleted" msgstr "gelöscht" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1168 msgid "Desktop View" msgstr "Desktop-Ansicht" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:258 msgid "Disable style for all forms" msgstr "Deaktiviere den Style für alle Formulare" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1296 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1333 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1400 msgid "Disable style for current form" msgstr "Deaktiviere den Style für das aktuelle Formular" #: admin/partials/cf7-customizer-admin-display.php:41 msgid "" "Do you like to get styler premium version for free? Then Enter your WP2LEADS pro " "license or get a license here!" msgstr "" "Möchtest du die Styler Premium Version for free? Dann gebe hier deine WP2LEADS Pro Lizenz-" "Daten ein oder hole dir hier jetzt eine Lizenz und " "verbinde deine WordPress-Plugins mit Klick-Tipp!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:184 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:303 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Do you want to save changes before leaving page?" msgstr "Möchtest du die Einstellungen speichern bevor du die Seite verlässt?" #: includes/lib/Cf7_Template.php:79 msgid "Dotted" msgstr "Gepunktet" #: includes/lib/Cf7_Template.php:81 msgid "Double" msgstr "Doppelt" #: admin/partials/cf7-customizer-admin-preview-mode.php:32 msgid "Duplicate form in second column" msgstr "Dupliziere Formular in der zweiten Spalte" #: admin/class-cf7-customizer-admin-ajax.php:396 msgid "enabled for all forms" msgstr "Aktiviere den Style für alle Formulare" #: public/class-cf7-customizer-public.php:471 #: admin/class-cf7-customizer-admin-ajax.php:133 #: admin/class-cf7-customizer-admin-ajax.php:230 #: admin/class-cf7-customizer-admin-ajax.php:237 #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:434 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 #: admin/class-cf7-customizer-admin-ajax.php:487 #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "Error" msgstr "Achtung!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:149 msgid "Exit Full Screen" msgstr "Vollbildmodus verlassen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:461 msgid "Font Family" msgstr "Schriftfamilie" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:455 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:834 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1090 msgid "Font Size" msgstr "Schriftgröße" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:514 msgid "Font Style" msgstr "Schrift-Stil" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:509 msgid "Font Weight" msgstr "Schrift-Stärke" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:530 msgid "Form BG Image & Colors" msgstr "Formular-Hintergrundbild & Farbe" #: admin/partials/cf7-customizer-admin-tabs.php:16 msgid "Form Customizing" msgstr "Formular-Anpassung" #: admin/class-cf7-customizer-admin-ajax.php:415 #: admin/class-cf7-customizer-admin-ajax.php:443 #: admin/class-cf7-customizer-admin-ajax.php:469 msgid "Form is not selected" msgstr "Kein Formular ausgewählt" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:689 msgid "Form Padding, Margin & Border" msgstr "Formular-Abstände, Rahmen & Schatten" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:446 msgid "Form Text" msgstr "Formular-Text, Links & Labels" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:148 msgid "Full Screen" msgstr "Vollbild" #: includes/lib/Cf7_Template.php:82 msgid "Groove" msgstr "Groove" #: includes/lib/Cf7_Template.php:31 msgid "Horizontal Length" msgstr "Horizontale Breite" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1081 msgid "Hover BG Color" msgstr "Hover-Hintergrundfarbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1121 msgid "Hover Color" msgstr "Hover-Farbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1069 msgid "Hover Text Color" msgstr "Hover-Text Farbe" #. Description of the plugin msgid "" "How to turn your contact form7 form into a converting and easy to use and " "pro styled contact form, \"survey\" lead generator or an eye catching form" msgstr "" "Wie Sie Ihren Kontakt form7 form in a-Konvertierung und einfach zu bedienen " "und pro-styled Kontaktformular, \"Umfrage\" lead-generator oder eine " "auffällige form" #. Author URI of the plugin msgid "https://saleswonder.biz" msgstr "https://saleswonder.biz" #. URI of the plugin msgid "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" msgstr "" "https://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-" "tipp-einfach-verbinden/" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:352 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:360 msgid "I'm happy with this! Save" msgstr "Ich bin glücklich sowie es ist! Bitte speichern!" #: admin/partials/cf7-customizer-admin-display.php:31 msgid "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" msgstr "" "If you are using page builders, like Thrive Architect, OptimizePress etc., " "please check our Knowledge Base page for fixing possible issues" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:275 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." ") and in premium version. You can not do it on archives pages (blog, " "products list etc) in this case you need to use global settings." msgstr "" "Wenn du deinen Style nur auf bestimmten Seiten mit dem <body>" " Tag laden möchtest, nimm die Einstellungen direkt auf der Seite vor. " "Dazu kannst du dort den \"CF7 Styler\" Button nutzen. \n" "Diese Funktion ist nur single post types wie Seiten, Beiträge, Produkte usw. " "und in der Premium Version verfügbar. Für Archiv-Seiten (Blog, Products " "Listen usw. steht diese Funktion nicht zur Verfügung. Nutze in diesem Fall " "die globale Einstellung direkt über diesem Text." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:314 msgid "" "If you need to load style scheme inside <body> tag only " "on some pages, you can do it on frontend using \"CF7 Styler\" button. This " "function is only available for single post types (pages, posts, products etc." "). You can not do it on archives pages (blog, products list etc) in this " "case you need to use global settings." msgstr "" "Wenn du deinen Style nur auf bestimmten Seiten mit dem <body>" " Tag laden möchtest, nimm die Einstellungen direkt auf der Seite vor. " "Dazu kannst du dort den \"CF7 Styler\" Button nutzen. \n" "Diese Funktion ist nur single post types wie Seiten, Beiträge, Produkte usw. " "und in der Premium Version verfügbar. Für Archiv-Seiten (Blog, Products " "Listen usw. steht diese Funktion nicht zur Verfügung. Nutze in diesem Fall " "die globale Einstellung direkt über diesem Text." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:547 msgid "" "Image background settings available in live mode only for Professional " "version. You can test it in preview mode \"current style\", but it will not " "be saved." msgstr "" "Die Hintergrund-Einstellungen bleiben der Pro Version vorbehalten. Du kannst " "jedoch das Ergebnis in der Vorschau-Ansicht testen, jedoch nicht speichern." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:663 msgid "Image Opacity" msgstr "Bild Deckkraft" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:586 msgid "Image Position" msgstr "Bild-Position" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:670 msgid "Image Size" msgstr "Füll-Eigenschaften" #: includes/lib/Cf7_Template.php:77 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:521 msgid "Inherit" msgstr "Vererbt" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:820 msgid "Input Fields" msgstr "Eingabe-Felder" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:402 msgid "Input style scheme title" msgstr "Benenne deinen Style" #: includes/lib/Cf7_Template.php:70 includes/lib/Cf7_Template.php:84 msgid "Inset" msgstr "Innen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:519 msgid "Italic" msgstr "Kursiv" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:497 msgid "Labels Color" msgstr "Label-Farbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:502 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1032 msgid "Labels Font Size" msgstr "Schriftgröße" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:492 msgid "Labels Settings" msgstr "Label-Einstellungen" #: admin/partials/cf7-customizer-admin-tabs.php:18 msgid "License" msgstr "Lizenz" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:839 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1095 msgid "Line Height" msgstr "Zeilenhöhe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:480 msgid "Links Color" msgstr "Link Farbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:485 msgid "Links Hover Color" msgstr "Link hover Farbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:476 msgid "Links Settings" msgstr "Link-Einstellungen" #: admin/partials/cf7-customizer-admin-preview-mode.php:7 #: admin/partials/cf7-customizer-admin-preview-mode.php:19 msgid "Live" msgstr "Live" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:265 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:299 msgid "Load styles in <body> tag" msgstr "Lade Styles im <body> Tag" #: public/class-cf7-customizer-public.php:348 msgid "Load styles inside <body> tag on this page" msgstr "" "Lade die Styles über den
    HTML-<body>-Tag in diese " "Seite." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1009 msgid "Make checkbox item one per line" msgstr "Nur eine Checkbox pro Zeile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1045 msgid "Make full width?" msgstr "In voller Breite anzeigen?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:822 msgid "Make input fields full width?" msgstr "Möchtest du die Eingabefelder in voller Breite?" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1019 msgid "Make radiobutton item one per line" msgstr "Nur ein Radiobutton pro Zeile" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:733 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:912 msgid "Margin" msgstr "Äußerer Abstand" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1170 msgid "Mobile View" msgstr "Mobile-Ansicht" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:828 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1015 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1025 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1051 msgid "NO" msgstr "Nein" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1450 msgid "No Contact Form 7 items for preview" msgstr "Kein Contact Form 7 Formular für die Vorschau vorhanden" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1387 msgid "" "No form is styled with style scheme, click \"Activate style for current " "form\" button to apply current scheme for this form." msgstr "" "Keines deiner Formulare ist gestyled. Klicke \"Aktiviere den Style für das " "aktuelle Formular\" um das ausgewählte Formular zu stylen." #: admin/class-cf7-customizer-admin-ajax.php:517 msgid "No plugin selected to install" msgstr "Kein Plugin zur Installation ausgewählt" #: admin/class-cf7-customizer-admin-ajax.php:230 msgid "No style scheme selected" msgstr "Kein Style ausgewählt" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:518 msgid "Normal" msgstr "Normal" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:520 msgid "Oblique" msgstr "Oblique" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:995 msgid "" "One per line styles for checkboxes and radiobuttons in live mode only for " "Professional version. You can test it in preview mode \"current style\", but " "it will not be saved." msgstr "" "Das untereinander Anordnen der Checkboxen und Radiobuttons bleibt der Pro " "Version vorbehalten. Du kannst jedoch das Ergebnis in der Vorschau-Ansicht " "testen, jedoch nicht speichern." #: includes/lib/Cf7_Template.php:61 msgid "Opacity" msgstr "Deckkraft" #: public/class-cf7-customizer-public.php:375 #: public/class-cf7-customizer-public.php:389 #: admin/class-cf7-customizer-admin.php:488 msgid "Open styler" msgstr "Öffne CF7 Styler" #: admin/class-cf7-customizer-admin.php:527 msgid "Opt-in to see account" msgstr "Opt-in um Konto zu sehen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:673 msgid "Original size" msgstr "original Größe" #: includes/lib/Cf7_Template.php:69 msgid "Outline" msgstr "Außen" #: includes/lib/Cf7_Template.php:85 msgid "Outset" msgstr "Outset" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:702 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:881 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1055 msgid "Padding" msgstr "Innere Abstand" #: admin/class-cf7-customizer-admin-ajax.php:133 msgid "Please input style scheme title" msgstr "Gib deinem Style einen Namen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1140 msgid "Preview" msgstr "Ansicht" #: admin/partials/cf7-customizer-admin-preview-mode.php:2 msgid "Preview mode" msgstr "Ansicht" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1343 msgid "Preview Unstyled" msgstr "Vorschau deaktivieren" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:968 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1110 msgid "Radius" msgstr "Radius" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:678 msgid "Repeat both" msgstr "vertikal & horizontal wiederholt" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:677 msgid "Repeat horizontal" msgstr "horizontal wiederholt" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:676 msgid "Repeat vertical" msgstr "vertikal wiederholt" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:383 msgid "Reset to default" msgstr "Zum Default-Style" #: includes/lib/Cf7_Template.php:83 msgid "Ridge" msgstr "Ridge" #: public/class-cf7-customizer-public.php:354 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:361 msgid "Save" msgstr "Speichern" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 msgid "Saved" msgstr "Gespeichert" #: admin/partials/cf7-customizer-admin-preview-mode.php:17 msgid "Second column view" msgstr "Ansicht der zweiten Spalte" #: admin/class-cf7-customizer-admin.php:459 msgid "Select style scheme for this form" msgstr "Wähle den Style für dieses Formular" #: admin/partials/cf7-customizer-admin-tabs.php:17 msgid "Settings" msgstr "Einstellungen" #: admin/class-cf7-customizer-admin-ajax.php:210 msgid "Settings saved as " msgstr "Dein Style wurde gespeichert unter " #: includes/lib/Cf7_Template.php:27 msgid "Shadow" msgstr "Schatten" #: includes/lib/Cf7_Template.php:56 msgid "Shadow Color" msgstr "Schatten-Farbe" #: includes/lib/Cf7_Template.php:66 msgid "Shadow Position" msgstr "Schatten-Position" #: includes/lib/Cf7_Template.php:78 msgid "Solid" msgstr "Solid" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:271 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:310 msgid "" "Some themes or page builders (fe. Thrive Architect, OptimizePress etc.) " "could remove inline styles inside <head> tag. Loading " "style scheme within <body> tag will fix this issue and " "show your forms styled." msgstr "" "Nutze diese Funktion wenn deine Formulare gar nicht gestyled werden. Da " "einige Themes oder Page Builder wie Thrive Architect, OptimizePress usw. die " "Styles aus dem <head> Tag entfernen können, gibt es die " "Option die Styles im <body> Tag zu Laden. Dadurch werden " "deine Formulare wieder gestyled." #: admin/partials/cf7-customizer-admin-preview-mode.php:5 msgid "Split mode" msgstr "Geteilt" #: admin/partials/cf7-customizer-admin-preview-mode.php:12 msgid "Split view" msgstr "Geteilte Ansicht" #: includes/lib/Cf7_Template.php:48 msgid "Spread radius" msgstr "Schatten-Größe" #: admin/partials/cf7-customizer-admin-tutorial.php:49 msgid "Start styling" msgstr "Beginne mit dem stylen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:337 msgid "Start with creating Default Style Scheme" msgstr "Beginne mit dem Erstellen des Standard-Styles" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1413 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1454 msgid "" "Start with creating your first Contact " "Form 7" msgstr "" "Beginne mit dem Erstellen deines ersten " "Contact Form 7 Formulars" #: admin/partials/cf7-customizer-admin-display.php:50 msgid "" "Start your 14-day free trial with all Professional functions here!" msgstr "" "Starte deine 14-Tage-Testversion mit allen Pro-Funktionen hier!" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:320 msgid "Step 1. Click \"Edit with Thrive architect\" link in admin page list" msgstr "" "Schritt 1: In der Admin-Seiten-Übersicht klicke auf den \"Edit with Thrive " "Architect\" -Link der Seite mit dem Formular." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:322 msgid "Step 2. Click \"Settings\" icon on right vertical menu" msgstr "" "Schritt 2: Klicke auf das \"Settings\" - Symbol im rechten vertikalen Menü" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:324 msgid "Step 3. \"Advanced settings\" => \"CSS in the <head> section\"" msgstr "" "Schritt 3: Klicke \"Erweiterte Einstellungen\" => \"CSS in the <" "head> section\"" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:326 msgid "" "Step 4. Make sure that \"Do not strip CSS from <head>\" is checked" msgstr "" "Schritt 4: Stelle sicher, dass \"Do not strip CSS from <head>" "\" ausgewählt ist" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:328 msgid "Step 5. Click \"SAVE WORK\" button" msgstr "Schritt 5. Klicke auf den \"SAVE WORK\" Button" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:290 msgid "Style all forms in one click globally" msgstr "Style alle Formulare global" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:296 msgid "Style each form with any style scheme individually" msgstr "Style jedes Formular in einem anderen Style" #: admin/class-cf7-customizer-admin-ajax.php:405 msgid "Style scheme disabled for all forms" msgstr "Deaktiviere den Style für alle Formulare" #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Style scheme disabled for this form" msgstr "Der Style ist für dieses Formular ist deaktiviert " #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 msgid "Style scheme enabled for this form" msgstr "Der Style ist für dieses Formular ist aktiviert " #: admin/class-cf7-customizer-admin-ajax.php:377 #: admin/class-cf7-customizer-admin-ajax.php:434 msgid "Style scheme is not selected" msgstr "Kein Style ausgewählt" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:180 msgid "Style schemes list" msgstr "CF7-Style-Auswahl" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1164 msgid "Style schemes preview" msgstr "Formular-Vorschau" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:146 msgid "Style schemes settings" msgstr "CF7-Style-Einstellungen" #: admin/class-cf7-customizer-admin-ajax.php:298 msgid "Style will be loaded in tag" msgstr "Style wird im <body> Tag geladen" #: admin/class-cf7-customizer-admin-ajax.php:301 msgid "Style will be loaded in tag" msgstr "Style wird im -Tag geladen" #: public/class-cf7-customizer-public.php:483 #: admin/class-cf7-customizer-admin-ajax.php:119 #: admin/class-cf7-customizer-admin-ajax.php:210 #: admin/class-cf7-customizer-admin-ajax.php:286 #: admin/class-cf7-customizer-admin-ajax.php:304 #: admin/class-cf7-customizer-admin-ajax.php:386 #: admin/class-cf7-customizer-admin-ajax.php:396 #: admin/class-cf7-customizer-admin-ajax.php:405 #: admin/class-cf7-customizer-admin-ajax.php:423 #: admin/class-cf7-customizer-admin-ajax.php:459 #: admin/class-cf7-customizer-admin-ajax.php:477 msgid "Success" msgstr "Erfolgreich" #: admin/class-cf7-customizer-admin.php:105 #: admin/class-cf7-customizer-admin.php:106 #: admin/class-cf7-customizer-admin.php:124 #: admin/class-cf7-customizer-admin.php:125 msgid "Support & KB" msgstr "Unterstützung & KB" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1169 msgid "Tablet View" msgstr "Tablet-Ansicht" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:450 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:848 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1064 msgid "Text Color" msgstr "Text Farbe" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1315 #, php-format msgid "" "This form is styled with %s globally, you can style it with " "current Style scheme." msgstr "" "Das derzeitige und andere Formulare sind mit dem %s-Style " "gestaltet. Du kannst mit einem Klick auch den derzeitigen Style aktivieren." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1311 #, php-format msgid "" "This form is styled with %s, you can style it with current " "Style scheme." msgstr "" "Der zur Zeit genutzte Style des Formulars ist %s. Du kannst " "auch unten klicken und den ausgewählten Style anwenden." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1307 msgid "" "This form is styled with current Style Scheme, you can disable it and use " "global settings." msgstr "" "Für dieses Formular ist der aktuelle Style aktiviert. Du kannst diesen Style " "deaktivieren und so zum globalen Style zurückkehren." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1319 msgid "" "This form not styled with any Style scheme, you can enable current Scheme " "for this form or set up global Style Scheme by clicking \"Use for all " "forms\" below scheme title." msgstr "" "Dieses Formular benutzt noch keinen Style. Du kannst den derzeitigen Style " "für das angezeigte Formular aktivieren oder mit dem Klick auf \"Aktiviere " "Style für alle Formulare\" den Style generell für alle Formulare festlegen. " "*Einzeln gestylte Formulare behalten ihren Style" #: admin/class-cf7-customizer-admin-ajax.php:386 msgid "This scheme is not existed" msgstr "Dieser Style existiert nicht" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:251 msgid "" "This Style Scheme enabled globally for all forms. If you want to use your " "theme's style for Contact Form 7 click \"Disable\" button." msgstr "" "Dieser Style ist global für alle Formulare aktiviert. Wenn du den Style " "deines Themes nutzen möchtest, dann klicken auf den \"Deaktiviere den Style " "für alle Formulare\"-Button. *Einzeln gestylte Formulare behalten ihren Style" #. Author of the plugin msgid "Tobias Conrad" msgstr "Tobias Conrad" #: admin/partials/cf7-customizer-admin-tutorial.php:36 msgid "Tutorial" msgstr "Tutorial" #: admin/partials/cf7-customizer-admin-preview-mode.php:8 #: admin/partials/cf7-customizer-admin-preview-mode.php:26 msgid "Unstyled" msgstr "Ohne Style" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:302 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:551 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:999 msgid "Upgrade to Pro" msgstr "Upgrade auf Pro" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:576 msgid "Upload Image" msgstr "Bild hochladen" #: includes/lib/Cf7_Template.php:36 msgid "Vertical Length" msgstr "Vertikaler Breite" #: admin/partials/cf7-customizer-admin-tutorial.php:34 msgid "Welcome" msgstr "Willkommen" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1105 msgid "Width" msgstr "Breite" #. Name of the plugin #: cf7-styler.php:138 admin/partials/cf7-customizer-admin-display.php:22 msgid "WOW Style Contact Form 7" msgstr "WOW Style Contact Form 7" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:825 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1012 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1022 #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1048 msgid "YES" msgstr "Ja" #: admin/partials/cf7-customizer-admin-tab-form-customize.php:405 msgid "" "You can create new style scheme with current scheme settings. If you want to " "create blank style schem unchek checkbox below." msgstr "" "Du kannst hier ein Style mit den aktuellen Einstellungen abspeichern. Wenn " "du einen leeren Style abspeichern möchtest entferne den Haken." #: admin/class-cf7-customizer-admin-ajax.php:237 msgid "You can not delete" msgstr "Löschen nicht möglich" #: public/class-cf7-customizer-public.php:471 msgid "You can not use this settings on this page type" msgstr "Du kannst diese Einstellungen nicht für diesen Seitentyp nutzen." #: admin/partials/cf7-customizer-admin-tab-form-customize.php:1411 msgid "You do not have any Contact Form 7 items" msgstr "Du hast noch kein Contact Form 7 Formular erstellt" #. No scheme enabled #: admin/partials/cf7-customizer-admin-tab-form-customize.php:233 msgid "" "You do not use any of Style Schemes for Contact Form 7. Click \"Use for all " "forms\" to use current Scheme globally." msgstr "" "Du verwendest derzeit keinen Style für deine Contact Form 7 Formulare. " "Klicke auf \"Aktiviere den Style für alle Formulare\", um den angezeigten " "Style global zu aktivieren. *Einzeln gestylte Formulare behalten ihren Style" languages/cf7-styler-de_DE.mo000064400000046565147600046700012030 0ustar00    ! m/   P , 5 @ Q ^b  ^2   "4:A_ x t   09@Ggm ~     #5 DPafOF  Y an   ,=5s" #z     '3EUflqw!     (T@B 4 @ !FK! !%!2!# "#0""T"w""""""""# "# /# ;#F#b#Y,$Z$$% %%%% %%% &&&|&&/&(&q'x'''`'() )))(*2+]7+++++a+@,U, \, h, v, , , ,,|,:- X- c-m--- --- -)- . ..=.T.<]./00 00 00)00(1]Y1F2 23)3:3C3[3j3 y33 33%334(41484K4 b4n4415Q5"689 9::::::: ::;; ; +;6;G;Z;,_;Q;;;0<F<d<v<<8<<(I=r==== M>W>i>>>>>>>>>> >?1?E?W? ]? g?s?%? ?#????@M@dAlA}AA-AgA;Bx5CIC^C`WD,DD+E)-E3WE1EEEEE3F DF eFqFF FFGHH#I6J SJaJ jJuJJJJ JJJJ_KAwK2KK L'LL`M- disable style scheme -- select -14-days Trial%s used for all forms globally, click "Use for all forms" if you want to use current Scheme.Activate style for all forms*Activate style for current formAs an alternative you can disable clean out the styles like on Thrive architect:BG ColorBG OpacityBackground ColorBackground ImageBefore using WOW Style Contact Form 7, you need to install and activate Contact Form 7 plugin.Blur radiusBorderBorder ColorBorder RadiusBorder TypeBorder WidthButton BorderButtonsBy Takayuki MiyoshiCF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.CF7 Form is not selectedCF7 StylerCancelCheckboxes & RadiobuttonsClean StylesClearCloseColorContact form 7 listContainCopy current style scheme settingsCoverCreateCreate multiple style schemesCreate new style scheme!Current StyleCurrently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Currently %s form is styled with %s. As in free version you can style only one form at a time and if you activate style for current form, style will be removed from other form.Custom CSSCustom CSS CodeDashedDefault SchemeDelete ImageDesktop ViewDisable style for all formsDisable style for current formDo you like to get styler premium version for free? Then Enter your WP2LEADS pro license or get a license here!Do you want to save changes before leaving page?DottedDoubleDuplicate form in second columnErrorExit Full ScreenFont FamilyFont SizeFont StyleFont WeightForm BG Image & ColorsForm CustomizingForm Padding, Margin & BorderForm TextForm is not selectedFull ScreenGrooveHorizontal LengthHover BG ColorHover ColorHover Text ColorHow to turn your contact form7 form into a converting and easy to use and pro styled contact form, "survey" lead generator or an eye catching formI'm happy with this! SaveIf you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesIf you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.) and in premium version. You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.If you need to load style scheme inside <body> tag only on some pages, you can do it on frontend using "CF7 Styler" button. This function is only available for single post types (pages, posts, products etc.). You can not do it on archives pages (blog, products list etc) in this case you need to use global settings.Image OpacityImage PositionImage SizeImage background settings available in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.InheritInput FieldsInput style scheme titleInsetItalicLabels ColorLabels Font SizeLabels SettingsLicenseLine HeightLinks ColorLinks Hover ColorLinks SettingsLiveLoad styles in <body> tagLoad styles inside <body> tag on this pageMake checkbox item one per lineMake full width?Make input fields full width?Make radiobutton item one per lineMarginMobile ViewNONo Contact Form 7 items for previewNo form is styled with style scheme, click "Activate style for current form" button to apply current scheme for this form.No plugin selected to installNo style scheme selectedNormalObliqueOne per line styles for checkboxes and radiobuttons in live mode only for Professional version. You can test it in preview mode "current style", but it will not be saved.OpacityOpen stylerOpt-in to see accountOriginal sizeOutlineOutsetPaddingPlease input style scheme titlePreviewPreview UnstyledPreview modeRadiusRepeat bothRepeat horizontalRepeat verticalReset to defaultRidgeSaveSavedSecond column viewSelect style scheme for this formSettingsSettings saved as ShadowShadow ColorShadow PositionSolidSome themes or page builders (fe. Thrive Architect, OptimizePress etc.) could remove inline styles inside <head> tag. Loading style scheme within <body> tag will fix this issue and show your forms styled.Split modeSplit viewSpread radiusStart stylingStart with creating Default Style SchemeStart with creating your first Contact Form 7Start your 14-day free trial with all Professional functions here!Step 1. Click "Edit with Thrive architect" link in admin page listStep 2. Click "Settings" icon on right vertical menuStep 3. "Advanced settings" => "CSS in the <head> section"Step 4. Make sure that "Do not strip CSS from <head>" is checkedStep 5. Click "SAVE WORK" buttonStyle all forms in one click globallyStyle each form with any style scheme individuallyStyle scheme disabled for all formsStyle scheme disabled for this formStyle scheme enabled for this formStyle scheme is not selectedStyle schemes listStyle schemes previewStyle schemes settingsStyle will be loaded in tagStyle will be loaded in tagSuccessSupport & KBTablet ViewText ColorThis Style Scheme enabled globally for all forms. If you want to use your theme's style for Contact Form 7 click "Disable" button.This form is styled with %s globally, you can style it with current Style scheme.This form is styled with %s, you can style it with current Style scheme.This form is styled with current Style Scheme, you can disable it and use global settings.This form not styled with any Style scheme, you can enable current Scheme for this form or set up global Style Scheme by clicking "Use for all forms" below scheme title.This scheme is not existedTobias ConradTutorialUnstyledUpgrade to ProUpload ImageVertical LengthWOW Style Contact Form 7WelcomeWidthYESYou can create new style scheme with current scheme settings. If you want to create blank style schem unchek checkbox below.You can not deleteYou can not use this settings on this page typeYou do not have any Contact Form 7 itemsYou do not use any of Style Schemes for Contact Form 7. Click "Use for all forms" to use current Scheme globally.deletedenabled for all formshttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/Project-Id-Version: Report-Msgid-Bugs-To: POT-Creation-Date: 2020-02-06 11:35+0000 PO-Revision-Date: 2023-04-15 16:02+0000 Last-Translator: Tobias support@saleswonder.biz Language-Team: German Language: de-DE Plural-Forms: nplurals=2; plural=n != 1; MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Loco https://localise.biz/ X-Loco-Version: 2.6.4; wp-6.1.2-alpha-55489- Style deaktivieren -- wähle -14 Tage testen%s-Style ist derzeit für alle Formulare aktiviert. Klicke auf den Button "Aktiviere den Style für alle Formulare" um den ausgewählten Style für alle Formulare zu nutzen. *Einzeln gestylte Formulare behalten ihren StyleAktiviere den Style für alle Formulare*Aktiviere den Style für das ausgewählte FormularAlternative kannst du das Entfernen der Styles deaktivieren wie im Beispiel Thrive Architect:Hintergrund-FarbeHintergrund-DeckkraftHintergrundfarbeHintergrundbildBevor mit WOW-Stil Contact Form 7, müssen Sie installieren und aktivieren Contact Form 7 plugin.Weichzeichner-RadiusRahmenRahmenfarbeRahmen-RadiusRahmen-StyleRahmenbreiteButton-RahmenButton(s)Von Takayuki MiyoshiContact Form 7 Styler erfordert ein installiertes und aktiviertes Contact Form 7-Plugin. Du kannst es hier herunterladen %s.Bitte wähle ein CF7-FormularCF7 StylerAbbrechenCheckboxen & RadiobuttonsAuf Default-Style zurücksetzenLeerenSchließenFarbeContact Form 7-AuswahlenthaltenKopiere die aktuellen Style-EinstellungengefülltErstellenErstelle mehrere Style-VorlagenNeuen Style erstellen!VorschauIm Moment ist das Formular %s ist mit Style %s gestyled. Nachdem du in der Free Version nur ein Formular zurzeit stylen kannst, wird bei Auswahl eines anderen Formulars das zuletzt gestylte Formular ungestyled.Im Moment ist das Formular %s ist mit Style %s gestyled. Nachdem du in der Free Version nur ein Formular zurzeit stylen kannst, wird bei Auswahl eines anderen Formulars das zuletzt gestylte Formular ungestyled.Benutzerdefiniertes CSSBenutzerdefinierter CSS-CodeGestrichelteStandard-StyleBild löschenDesktop-AnsichtDeaktiviere den Style für alle FormulareDeaktiviere den Style für das aktuelle FormularMöchtest du die Styler Premium Version for free? Dann gebe hier deine WP2LEADS Pro Lizenz-Daten ein oder hole dir hier jetzt eine Lizenz und verbinde deine WordPress-Plugins mit Klick-Tipp!Möchtest du die Einstellungen speichern bevor du die Seite verlässt?GepunktetDoppeltDupliziere Formular in der zweiten SpalteAchtung!Vollbildmodus verlassenSchriftfamilieSchriftgrößeSchrift-StilSchrift-StärkeFormular-Hintergrundbild & FarbeFormular-AnpassungFormular-Abstände, Rahmen & SchattenFormular-Text, Links & LabelsKein Formular ausgewähltVollbildGrooveHorizontale BreiteHover-HintergrundfarbeHover-FarbeHover-Text FarbeWie Sie Ihren Kontakt form7 form in a-Konvertierung und einfach zu bedienen und pro-styled Kontaktformular, "Umfrage" lead-generator oder eine auffällige formIch bin glücklich sowie es ist! Bitte speichern!If you are using page builders, like Thrive Architect, OptimizePress etc., please check our Knowledge Base page for fixing possible issuesWenn du deinen Style nur auf bestimmten Seiten mit dem <body> Tag laden möchtest, nimm die Einstellungen direkt auf der Seite vor. Dazu kannst du dort den "CF7 Styler" Button nutzen. Diese Funktion ist nur single post types wie Seiten, Beiträge, Produkte usw. und in der Premium Version verfügbar. Für Archiv-Seiten (Blog, Products Listen usw. steht diese Funktion nicht zur Verfügung. Nutze in diesem Fall die globale Einstellung direkt über diesem Text.Wenn du deinen Style nur auf bestimmten Seiten mit dem <body> Tag laden möchtest, nimm die Einstellungen direkt auf der Seite vor. Dazu kannst du dort den "CF7 Styler" Button nutzen. Diese Funktion ist nur single post types wie Seiten, Beiträge, Produkte usw. und in der Premium Version verfügbar. Für Archiv-Seiten (Blog, Products Listen usw. steht diese Funktion nicht zur Verfügung. Nutze in diesem Fall die globale Einstellung direkt über diesem Text.Bild DeckkraftBild-PositionFüll-EigenschaftenDie Hintergrund-Einstellungen bleiben der Pro Version vorbehalten. Du kannst jedoch das Ergebnis in der Vorschau-Ansicht testen, jedoch nicht speichern.VererbtEingabe-FelderBenenne deinen StyleInnenKursivLabel-FarbeSchriftgrößeLabel-EinstellungenLizenzZeilenhöheLink FarbeLink hover FarbeLink-EinstellungenLiveLade Styles im <body> TagLade die Styles über den
    HTML-<body>-Tag in diese Seite.Nur eine Checkbox pro ZeileIn voller Breite anzeigen?Möchtest du die Eingabefelder in voller Breite?Nur ein Radiobutton pro ZeileÄußerer AbstandMobile-AnsichtNeinKein Contact Form 7 Formular für die Vorschau vorhandenKeines deiner Formulare ist gestyled. Klicke "Aktiviere den Style für das aktuelle Formular" um das ausgewählte Formular zu stylen.Kein Plugin zur Installation ausgewähltKein Style ausgewähltNormalObliqueDas untereinander Anordnen der Checkboxen und Radiobuttons bleibt der Pro Version vorbehalten. Du kannst jedoch das Ergebnis in der Vorschau-Ansicht testen, jedoch nicht speichern.DeckkraftÖffne CF7 StylerOpt-in um Konto zu sehenoriginal GrößeAußenOutsetInnere AbstandGib deinem Style einen NamenAnsichtVorschau deaktivierenAnsichtRadiusvertikal & horizontal wiederholthorizontal wiederholtvertikal wiederholtZum Default-StyleRidgeSpeichernGespeichertAnsicht der zweiten SpalteWähle den Style für dieses FormularEinstellungenDein Style wurde gespeichert unter SchattenSchatten-FarbeSchatten-PositionSolidNutze diese Funktion wenn deine Formulare gar nicht gestyled werden. Da einige Themes oder Page Builder wie Thrive Architect, OptimizePress usw. die Styles aus dem <head> Tag entfernen können, gibt es die Option die Styles im <body> Tag zu Laden. Dadurch werden deine Formulare wieder gestyled.GeteiltGeteilte AnsichtSchatten-GrößeBeginne mit dem stylenBeginne mit dem Erstellen des Standard-StylesBeginne mit dem Erstellen deines ersten Contact Form 7 FormularsStarte deine 14-Tage-Testversion mit allen Pro-Funktionen hier!Schritt 1: In der Admin-Seiten-Übersicht klicke auf den "Edit with Thrive Architect" -Link der Seite mit dem Formular.Schritt 2: Klicke auf das "Settings" - Symbol im rechten vertikalen MenüSchritt 3: Klicke "Erweiterte Einstellungen" => "CSS in the <head> section"Schritt 4: Stelle sicher, dass "Do not strip CSS from <head>" ausgewählt istSchritt 5. Klicke auf den "SAVE WORK" ButtonStyle alle Formulare globalStyle jedes Formular in einem anderen StyleDeaktiviere den Style für alle FormulareDer Style ist für dieses Formular ist deaktiviert Der Style ist für dieses Formular ist aktiviert Kein Style ausgewähltCF7-Style-AuswahlFormular-VorschauCF7-Style-EinstellungenStyle wird im <body> Tag geladenStyle wird im -Tag geladenErfolgreichUnterstützung & KBTablet-AnsichtText FarbeDieser Style ist global für alle Formulare aktiviert. Wenn du den Style deines Themes nutzen möchtest, dann klicken auf den "Deaktiviere den Style für alle Formulare"-Button. *Einzeln gestylte Formulare behalten ihren StyleDas derzeitige und andere Formulare sind mit dem %s-Style gestaltet. Du kannst mit einem Klick auch den derzeitigen Style aktivieren.Der zur Zeit genutzte Style des Formulars ist %s. Du kannst auch unten klicken und den ausgewählten Style anwenden.Für dieses Formular ist der aktuelle Style aktiviert. Du kannst diesen Style deaktivieren und so zum globalen Style zurückkehren.Dieses Formular benutzt noch keinen Style. Du kannst den derzeitigen Style für das angezeigte Formular aktivieren oder mit dem Klick auf "Aktiviere Style für alle Formulare" den Style generell für alle Formulare festlegen. *Einzeln gestylte Formulare behalten ihren StyleDieser Style existiert nichtTobias ConradTutorialOhne StyleUpgrade auf ProBild hochladenVertikaler BreiteWOW Style Contact Form 7WillkommenBreiteJaDu kannst hier ein Style mit den aktuellen Einstellungen abspeichern. Wenn du einen leeren Style abspeichern möchtest entferne den Haken.Löschen nicht möglichDu kannst diese Einstellungen nicht für diesen Seitentyp nutzen.Du hast noch kein Contact Form 7 Formular erstelltDu verwendest derzeit keinen Style für deine Contact Form 7 Formulare. Klicke auf "Aktiviere den Style für alle Formulare", um den angezeigten Style global zu aktivieren. *Einzeln gestylte Formulare behalten ihren StylegelöschtAktiviere den Style für alle Formularehttps://saleswonder.bizhttps://saleswonder.biz/blog/4free-contact-form-7-cf7-formular-und-klick-tipp-einfach-verbinden/public/css/cf7-customizer-public.css000064400000003752147600046700013474 0ustar00/** * All of the CSS for your public-facing functionality should be * included in this file. */ #cf7cstmzr_frontend { position: fixed; bottom: 0; background-color: #fff; padding: 10px; right: -303px; border: 1px solid #1e73be; width: 100%; max-width: 300px; z-index: 6000; transition: all 0.6s; } #cf7cstmzr_frontend.active { right: 0; } #cf7cstmzr_frontend_togler { line-height: 20px; position: absolute; bottom: 20px; width: 110px; left: 0; transform: translateX(-115px); border: 1px solid #1e73be; padding: 5px; background-color: rgba(255,255,255,0.6); cursor: pointer; font-size: 14px; } #cf7cstmzr_form_frontend_togler, .cf7cstmzr_form_frontend_link { display: inline-block; line-height: 20px; border: 1px solid #1e73be; padding: 5px; background-color: rgba(255,255,255,1); cursor: pointer; font-size: 14px; margin-bottom: 10px; text-decoration: none!important; color: #333; } #cf7cstmzr_form_frontend_togler:hover, .cf7cstmzr_form_frontend_link:hover, #cf7cstmzr_frontend_togler:hover { color: #1e73be; background-color: rgba(255,255,255,0.9); } .cf7cstmzr-button { outline: transparent !important; display: inline-block; width: 100%; background-color: #007bff !important; padding: 10px 15px !important; color: #fff !important; line-height: 1.2; border: none!important; } .cf7cstmzr-button:hover { background-color: #0067cd !important; color: #fff !important; border: none!important; } .cf7cstmzr-link { outline: transparent !important; font-weight: 400!important; display: inline-block; width: 100%; background-color: transparent !important; padding: 5px 5px 0 5px !important; color: #999 !important; line-height: 1.2; font-size: 13px; border: none!important; } .cf7cstmzr-link:hover { background-color: transparent !important; color: #007bff !important; border: none!important; }public/img/vertical.png000064400000000133147600046700011116 0ustar00PNG  IHDR~Խ"IDAT(Sc8s 9j&^3~γIENDB`public/img/horizontal.png000064400000000150147600046700011475 0ustar00PNG  IHDRhιPLTEGpLtRNS@fIDATx^c8\y z#`IENDB`public/js/split.min.js000064400000013420147600046700010715 0ustar00/*! Split.js - v1.5.11 */ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Split=t()}(this,function(){"use strict";var L=window,T=L.document,N="addEventListener",R="removeEventListener",q="getBoundingClientRect",H="horizontal",I=function(){return!1},W=L.attachEvent&&!L[N],i=["","-webkit-","-moz-","-o-"].filter(function(e){var t=T.createElement("div");return t.style.cssText="width:"+e+"calc(9px)",!!t.style.length}).shift()+"calc",s=function(e){return"string"==typeof e||e instanceof String},X=function(e){if(s(e)){var t=T.querySelector(e);if(!t)throw new Error("Selector "+e+" did not match a DOM element");return t}return e},Y=function(e,t,n){var r=e[t];return void 0!==r?r:n},G=function(e,t,n,r){if(t){if("end"===r)return 0;if("center"===r)return e/2}else if(n){if("start"===r)return 0;if("center"===r)return e/2}return e},J=function(e,t){var n=T.createElement("div");return n.className="gutter gutter-"+t,n},K=function(e,t,n){var r={};return s(t)?r[e]=t:r[e]=W?t+"%":i+"("+t+"% - "+n+"px)",r},P=function(e,t){var n;return(n={})[e]=t+"px",n};return function(e,i){void 0===i&&(i={});var u,t,s,o,r,a,l=e;Array.from&&(l=Array.from(l));var c=X(l[0]).parentNode,n=getComputedStyle?getComputedStyle(c):null,f=n?n.flexDirection:null,m=Y(i,"sizes")||l.map(function(){return 100/l.length}),h=Y(i,"minSize",100),d=Array.isArray(h)?h:l.map(function(){return h}),g=Y(i,"expandToMin",!1),v=Y(i,"gutterSize",10),p=Y(i,"gutterAlign","center"),y=Y(i,"snapOffset",30),z=Y(i,"dragInterval",1),S=Y(i,"direction",H),b=Y(i,"cursor",S===H?"col-resize":"row-resize"),_=Y(i,"gutter",J),E=Y(i,"elementStyle",K),w=Y(i,"gutterStyle",P);function k(t,e,n,r){var i=E(u,e,n,r);Object.keys(i).forEach(function(e){t.style[e]=i[e]})}function x(){return a.map(function(e){return e.size})}function M(e){return"touches"in e?e.touches[0][t]:e[t]}function U(e){var t=a[this.a],n=a[this.b],r=t.size+n.size;t.size=e/this.size*r,n.size=r-e/this.size*r,k(t.element,t.size,this._b,t.i),k(n.element,n.size,this._c,n.i)}function O(){var e=a[this.a].element,t=a[this.b].element,n=e[q](),r=t[q]();this.size=n[u]+r[u]+this._b+this._c,this.start=n[s],this.end=n[o]}function C(s){var o=function(e){if(!getComputedStyle)return null;var t=getComputedStyle(e);if(!t)return null;var n=e[r];return 0===n?null:n-=S===H?parseFloat(t.paddingLeft)+parseFloat(t.paddingRight):parseFloat(t.paddingTop)+parseFloat(t.paddingBottom)}(c);if(null===o)return s;if(d.reduce(function(e,t){return e+t},0)>o)return s;var a=0,u=[],e=s.map(function(e,t){var n=o*e/100,r=G(v,0===t,t===s.length-1,p),i=d[t]+r;return n=this.size-(r.minSize+y+this._c)&&(t=this.size-(r.minSize+this._c)),U.call(this,t),Y(i,"onDrag",I)())}.bind(t),t.stop=function(){var e=this,t=a[e.a].element,n=a[e.b].element;e.dragging&&Y(i,"onDragEnd",I)(x()),e.dragging=!1,L[R]("mouseup",e.stop),L[R]("touchend",e.stop),L[R]("touchcancel",e.stop),L[R]("mousemove",e.move),L[R]("touchmove",e.move),e.stop=null,e.move=null,t[R]("selectstart",I),t[R]("dragstart",I),n[R]("selectstart",I),n[R]("dragstart",I),t.style.userSelect="",t.style.webkitUserSelect="",t.style.MozUserSelect="",t.style.pointerEvents="",n.style.userSelect="",n.style.webkitUserSelect="",n.style.MozUserSelect="",n.style.pointerEvents="",e.gutter.style.cursor="",e.parent.style.cursor="",T.body.style.cursor=""}.bind(t),L[N]("mouseup",t.stop),L[N]("touchend",t.stop),L[N]("touchcancel",t.stop),L[N]("mousemove",t.move),L[N]("touchmove",t.move),n[N]("selectstart",I),n[N]("dragstart",I),r[N]("selectstart",I),r[N]("dragstart",I),n.style.userSelect="none",n.style.webkitUserSelect="none",n.style.MozUserSelect="none",n.style.pointerEvents="none",r.style.userSelect="none",r.style.webkitUserSelect="none",r.style.MozUserSelect="none",r.style.pointerEvents="none",t.gutter.style.cursor=b,t.parent.style.cursor=b,T.body.style.cursor=b,O.call(t),t.dragOffset=M(e)-t.end}}S===H?(u="width",t="clientX",s="left",o="right",r="clientWidth"):"vertical"===S&&(u="height",t="clientY",s="top",o="bottom",r="clientHeight"),m=C(m);var A=[];function j(e){var t=e.i===A.length,n=t?A[e.i-1]:A[e.i];O.call(n);var r=t?n.size-e.minSize-n._c:e.minSize+n._b;U.call(n,r)}function F(e){var s=C(e);s.forEach(function(e,t){if(0 public/index.php000064400000000032147600046700007641 0ustar00 */ class Cf7_Customizer_Public { /** * The ID of this plugin. * * @since 1.0.0 * @access private * @var string $plugin_name The ID of this plugin. */ private $plugin_name; /** * The version of this plugin. * * @since 1.0.0 * @access private * @var string $version The current version of this plugin. */ private $version; /** * Initialize the class and set its properties. * * @since 1.0.0 * @param string $plugin_name The name of the plugin. * @param string $version The version of this plugin. */ public function __construct( $plugin_name, $version ) { $this->plugin_name = $plugin_name; $this->version = $version; } /** * Register the stylesheets for the public-facing side of the site. * * @since 1.0.0 */ public function enqueue_styles() { /** * This function is provided for demonstration purposes only. * * An instance of this class should be passed to the run() function * defined in Cf7_Customizer_Loader as all of the hooks are defined * in that particular class. * * The Cf7_Customizer_Loader will then create the relationship * between the defined hooks and the functions defined in this * class. */ wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/cf7-customizer-public.css', array(), $this->version . time(), 'all' ); $permalink_structure = get_option('permalink_structure'); if (!empty($permalink_structure)) { $form_id = sanitize_text_field(get_query_var( 'cf7cstmzr-form' )); } else { if (!empty($_GET['cf7cstmzr_page']) && !empty($_GET['form_id'])) { $form_id = filter_input( INPUT_GET, 'form_id', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); } } $plugin_version = Cf7_License::get_license_version(); if ($form_id) { // Backend testing $style_scheme_slug = !empty($_GET['style_scheme']) ? filter_input( INPUT_GET, 'style_scheme', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : 'default'; $style_schemes = get_option('cf7cstmzr_style_schemes', array()); $is_styled = get_option('cf7cstmzr-preview-styled', true); $preview_mode = get_option('cf7cstmzr-preview-mode', false); if (!empty($preview_mode)) { if ('unstyled' === $preview_mode) { $is_styled = false; } elseif ('current-style' === $preview_mode) { $is_styled = true; } else { $is_styled = true; } } if ($is_styled) { if ('split-mode' === $preview_mode) { $temp_styles = get_option('cf7cstmzr_style_schemes_preview', false); $live_style = get_post_meta( $form_id, 'cf7cstmzr_style_scheme', true ); if (empty($live_style) && 'free' !== $plugin_version) { $live_style = get_option('cf7cstmzr_enabled_globally', false); } if (!empty($live_style)) { $live_styles = array( 'preview' => array( 'title' => 'preview', 'scheme' => $style_schemes[$live_style]['scheme'], ), ); } else { $live_styles = array( 'preview' => array( 'title' => 'preview', 'scheme' => array(), ), ); } } elseif ('current-style' === $preview_mode) { $temp_styles = get_option('cf7cstmzr_style_schemes_preview', false); } else { $temp_style = get_post_meta( $form_id, 'cf7cstmzr_style_scheme', true ); if (empty($temp_style) && 'free' !== $plugin_version) { $temp_style = get_option('cf7cstmzr_enabled_globally', false); } if (!empty($temp_style)) { $temp_styles = array( 'preview' => array( 'title' => 'preview', 'scheme' => $style_schemes[$temp_style]['scheme'], ), ); } else { $temp_styles = array( 'preview' => array( 'title' => 'preview', 'scheme' => array(), ), ); } } if (!empty($live_styles)) { if (!empty($live_styles['preview']['scheme'])) { if ('free' === $plugin_version && 'current-style' !== $preview_mode) { if (!empty($live_styles['preview']["scheme"]["form"]["bg"]["img"])) $live_styles['preview']["scheme"]["form"]["bg"]["img"] = ''; if (!empty($live_styles['preview']["scheme"]["form"]["bg"]["img-position"])) $live_styles['preview']["scheme"]["form"]["bg"]["img-position"] = ''; if (!empty($live_styles['preview']["scheme"]["form"]["bg"]["img-opacity"])) $live_styles['preview']["scheme"]["form"]["bg"]["img-opacity"] = ''; if (!empty($live_styles['preview']["scheme"]["form"]["bg"]["img-size"])) $live_styles['preview']["scheme"]["form"]["bg"]["img-size"] = ''; if (!empty($live_styles['preview']["scheme"]["checkbox"]["full-width"])) $live_styles['preview']["scheme"]["checkbox"]["full-width"] = ''; if (!empty($live_styles['preview']["scheme"]["radiobutton"]["full-width"])) $live_styles['preview']["scheme"]["radiobutton"]["full-width"] = ''; } $live_custom_css = Cf7_Style_Scheme::get_inline_style_scheme($live_styles, 'preview', array(), array(), '.live-style '); } else { $live_custom_css = ''; } } if (!empty($temp_styles)) { if (!empty($temp_styles['preview']['scheme'])) { if ('free' === $plugin_version && ('current-style' !== $preview_mode && 'split-mode' !== $preview_mode)) { if (!empty($temp_styles['preview']["scheme"]["form"]["bg"]["img"])) $temp_styles['preview']["scheme"]["form"]["bg"]["img"] = ''; if (!empty($temp_styles['preview']["scheme"]["form"]["bg"]["img-position"])) $temp_styles['preview']["scheme"]["form"]["bg"]["img-position"] = ''; if (!empty($temp_styles['preview']["scheme"]["form"]["bg"]["img-opacity"])) $temp_styles['preview']["scheme"]["form"]["bg"]["img-opacity"] = ''; if (!empty($temp_styles['preview']["scheme"]["form"]["bg"]["img-size"])) $temp_styles['preview']["scheme"]["form"]["bg"]["img-size"] = ''; if (!empty($temp_styles['preview']["scheme"]["checkbox"]["full-width"])) $temp_styles['preview']["scheme"]["checkbox"]["full-width"] = ''; if (!empty($temp_styles['preview']["scheme"]["radiobutton"]["full-width"])) $temp_styles['preview']["scheme"]["radiobutton"]["full-width"] = ''; } if ('split-mode' === $preview_mode) { $custom_css = Cf7_Style_Scheme::get_inline_style_scheme($temp_styles, 'preview', array(), array(), '.current-style '); } else { $custom_css = Cf7_Style_Scheme::get_inline_style_scheme($temp_styles, 'preview', array(), array()); } } else { $custom_css = ''; } } else { if ('free' === $plugin_version) { if (!empty($style_schemes[$style_scheme_slug]["scheme"]["form"]["bg"]["img"])) $style_schemes[$style_scheme_slug]["scheme"]["form"]["bg"]["img"] = ''; if (!empty($style_schemes[$style_scheme_slug]["scheme"]["form"]["bg"]["img-position"])) $style_schemes[$style_scheme_slug]["scheme"]["form"]["bg"]["img-position"] = ''; if (!empty($style_schemes[$style_scheme_slug]["scheme"]["form"]["bg"]["img-opacity"])) $style_schemes[$style_scheme_slug]["scheme"]["form"]["bg"]["img-opacity"] = ''; if (!empty($style_schemes[$style_scheme_slug]["scheme"]["form"]["bg"]["img-size"])) $style_schemes[$style_scheme_slug]["scheme"]["form"]["bg"]["img-size"] = ''; if (!empty($style_schemes[$style_scheme_slug]["scheme"]["checkbox"]["full-width"])) $style_schemes[$style_scheme_slug]["scheme"]["checkbox"]["full-width"] = ''; if (!empty($style_schemes[$style_scheme_slug]["scheme"]["radiobutton"]["full-width"])) $style_schemes[$style_scheme_slug]["scheme"]["radiobutton"]["full-width"] = ''; } $custom_css = Cf7_Style_Scheme::get_inline_style_scheme($style_schemes, $style_scheme_slug); } wp_add_inline_style( $this->plugin_name, $custom_css ); if (!empty($live_custom_css)) { wp_add_inline_style( $this->plugin_name, $live_custom_css ); } } // delete_option('cf7cstmzr_style_schemes_preview'); } else { // Frontend use $is_body_tag = false; if ('free' !== $plugin_version) { $is_body_tag = get_option('cf7cstmzr-load-body-tag', false); } if (empty($is_body_tag)) { $enabled_globally = false; $enabled_globally_option = get_option('cf7cstmzr_enabled_globally', false); if (!empty($enabled_globally_option)) { $enabled_globally = 'default'; if ('free' !== $plugin_version) { $enabled_globally = $enabled_globally_option; } } $globally_styled_forms = Cf7_Style_Scheme::get_globally_styled_forms(); $individually_styled_forms = Cf7_Style_Scheme::get_individually_styled_forms(); $style_schemes = get_option('cf7cstmzr_style_schemes', array()); if ($enabled_globally && 'free' !== $plugin_version) { if ('free' === $plugin_version) { if (!empty($style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img"])) $style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-position"])) $style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-position"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-opacity"])) $style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-opacity"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-size"])) $style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-size"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["checkbox"]["full-width"])) $style_schemes[$enabled_globally]["scheme"]["checkbox"]["full-width"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["radiobutton"]["full-width"])) $style_schemes[$enabled_globally]["scheme"]["radiobutton"]["full-width"] = ''; } $custom_css = Cf7_Style_Scheme::get_inline_style_scheme($style_schemes, $enabled_globally, array_keys($globally_styled_forms)); wp_add_inline_style( $this->plugin_name, $custom_css ); } // if ('free' !== $plugin_version && !empty($individually_styled_forms)) { if (!empty($individually_styled_forms)) { foreach ($individually_styled_forms as $form_id => $individually_style) { if ('free' === $plugin_version) { if (!empty($style_schemes[$individually_style]["scheme"]["form"]["bg"]["img"])) $style_schemes[$individually_style]["scheme"]["form"]["bg"]["img"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-position"])) $style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-position"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-opacity"])) $style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-opacity"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-size"])) $style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-size"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["checkbox"]["full-width"])) $style_schemes[$individually_style]["scheme"]["checkbox"]["full-width"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["radiobutton"]["full-width"])) $style_schemes[$individually_style]["scheme"]["radiobutton"]["full-width"] = ''; } $custom_css = Cf7_Style_Scheme::get_inline_style_scheme($style_schemes, $individually_style, array($form_id)); wp_add_inline_style( $this->plugin_name, $custom_css ); } } } } } /** * Register the JavaScript for the public-facing side of the site. * * @since 1.0.0 */ public function enqueue_scripts() { $permalink_structure = get_option('permalink_structure'); if (!empty($permalink_structure)) { $form_id = sanitize_text_field(get_query_var( 'cf7cstmzr-form' )); } else { if (!empty($_GET['cf7cstmzr_page']) && !empty($_GET['form_id'])) { $form_id = filter_input( INPUT_GET, 'form_id', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); } } if ($form_id) { wp_enqueue_script('cf7cstmzr-split', plugin_dir_url( __FILE__ ) . 'js/split.min.js', array('jquery'), '1.5.11'); } wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/cf7-customizer-public.js', array( 'jquery' ), $this->version . time(), true ); wp_localize_script( $this->plugin_name, 'cf7cstmzrJsObj', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), )); } public function add_wrapper($output, $tag, $attr, $m) { if ('contact-form-7' === sanitize_text_field($tag)) { $permalink_structure = get_option('permalink_structure'); if (!empty($permalink_structure)) { $form_id = sanitize_text_field(get_query_var( 'cf7cstmzr-form' )); } else { if (!empty(sanitize_text_field($_GET['cf7cstmzr_page'])) && !empty(sanitize_text_field($_GET['form_id']))) { $form_id = filter_input( INPUT_GET, 'form_id', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); } } $plugin_version = Cf7_License::get_license_version(); $output_before = ''; $is_body_tag = false; $is_form_exists = !empty($attr["id"]) ? get_post($attr["id"]) : null; if (empty($is_form_exists) && !empty($attr["id"])) { if (function_exists('wpcf7_get_contact_form_by_hash')) { $is_form_exists = wpcf7_get_contact_form_by_hash( $attr["id"] ); } if ( ! $is_form_exists && function_exists('wpcf7_contact_form') ) { $is_form_exists = wpcf7_contact_form( $attr["id"] ); } } if ('free' !== $plugin_version) { $is_body_tag = get_option('cf7cstmzr-load-body-tag', false); } if ($form_id) { } else { global $post; if ('free' !== $plugin_version) { $is_page_body_tag = get_post_meta( $post->ID, 'cf7cstmzr-load-body-tag', true ); if (!empty($is_page_body_tag)) { $is_body_tag = $is_page_body_tag; } } if ('free' !== $plugin_version && current_user_can('administrator') && (is_single() || is_page())) { wp_enqueue_style( 'dashicons' ); $frontend_control_action = did_action('cf7cstmzr_frontend_control'); ob_start(); if (1 > $frontend_control_action && $is_form_exists) { do_action('cf7cstmzr_frontend_control'); ?>
    $num_action) { do_action('cf7cstmzr_load_body_tag'); $enabled_globally = false; $enabled_globally_option = get_option('cf7cstmzr_enabled_globally', false); if (!empty($enabled_globally_option)) { $enabled_globally = 'default'; if ('free' !== $plugin_version) { $enabled_globally = $enabled_globally_option; } } $globally_styled_forms = Cf7_Style_Scheme::get_globally_styled_forms(); $individually_styled_forms = Cf7_Style_Scheme::get_individually_styled_forms(); $style_schemes = get_option('cf7cstmzr_style_schemes', array()); if ($enabled_globally && 'free' !== $plugin_version) { if ('free' === $plugin_version) { if (!empty($style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img"])) $style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-position"])) $style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-position"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-opacity"])) $style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-opacity"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-size"])) $style_schemes[$enabled_globally]["scheme"]["form"]["bg"]["img-size"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["checkbox"]["full-width"])) $style_schemes[$enabled_globally]["scheme"]["checkbox"]["full-width"] = ''; if (!empty($style_schemes[$enabled_globally]["scheme"]["radiobutton"]["full-width"])) $style_schemes[$enabled_globally]["scheme"]["radiobutton"]["full-width"] = ''; } $custom_css = ''; $output_before .= $custom_css; } // if ('free' !== $plugin_version && !empty($individually_styled_forms)) { if (!empty($individually_styled_forms)) { foreach ($individually_styled_forms as $form_id => $individually_style) { if ('free' === $plugin_version) { if (!empty($style_schemes[$individually_style]["scheme"]["form"]["bg"]["img"])) $style_schemes[$individually_style]["scheme"]["form"]["bg"]["img"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-position"])) $style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-position"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-opacity"])) $style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-opacity"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-size"])) $style_schemes[$individually_style]["scheme"]["form"]["bg"]["img-size"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["checkbox"]["full-width"])) $style_schemes[$individually_style]["scheme"]["checkbox"]["full-width"] = ''; if (!empty($style_schemes[$individually_style]["scheme"]["radiobutton"]["full-width"])) $style_schemes[$individually_style]["scheme"]["radiobutton"]["full-width"] = ''; } $custom_css = ''; $output_before .= $custom_css; } } } } } $output_after = ''; if ($is_form_exists) { $output_before .= '
    '; $output_after .= '
    '; } $output = $output_before . $output . $output_after; } return $output; } public function save_frontend_settings() { $loadBodyTag = !empty($_POST['loadBodyTag']) ? sanitize_text_field($_POST['loadBodyTag']) : false; $postId = !empty($_POST['postId']) ? sanitize_text_field($_POST['postId']) : false; if (empty($postId)) { $response = array('success' => 0, 'error' => 1, 'message' => __('Error', 'cf7-styler') . ': ' . __('You can not use this settings on this page type', 'cf7-styler')); echo json_encode($response); wp_die(); } if ($loadBodyTag && 'true' === $loadBodyTag) { update_post_meta( $postId, 'cf7cstmzr-load-body-tag', 1 ); } else { delete_post_meta( $postId, 'cf7cstmzr-load-body-tag' ); } $response = array('success' => 1, 'error' => 0, 'message' => __('Success', 'cf7-styler') . ': ' . __('Saved', 'cf7-styler')); echo json_encode($response); wp_die(); } } README.txt000064400000047223147600046700006256 0ustar00=== Design for Contact Form 7 Style WordPress Plugin - CF7 WOW Styler === Contributors: Tobias_Conrad, freemius, subscribetech Donate link: https://saleswonder.biz/?=utm_source=wp_org-plugin&utm_medium=plugin&utm_campaign=Cf7-Styler Tags: contact form 7, cf7, contactform7, Contact Form 7 style, Design Tested up to: 6.7 Stable tag: 1.7.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Save your time, get more users using your forms and make the forms match your brand with style and uniqueness by using the WOW Styler. == Description == Both non-tech and tech users can highly benefit from our step by step knowledge base! Get fast styled your Contact Form 7 form with the CF7 WOW Styler plugin and the video tutorials! [Use video tutorials and knowledge base](https://design-for-contact-form-7-wow-styler.tawk.help/) https://www.youtube.com/watch?v=maY80TCYVWA https://www.youtube.com/watch?v=cZHr7bLMvho https://www.youtube.com/watch?v=1eMYnhXzTaU Looking to change the form styles with the most famous Contact Form 7 plugin without having coding skills? Here is CF7 WOW Styler. With its help, you can fully customize the Contact form 7 plugin’s forms design by changing the form colors, shapes, shadows, backgrounds, etc. All you have to do is create your desired style and design in the browser as a form output without adding any extra code to the page. == Problems without the WOW Styler: == - Less visitors use your forms, that means less contacts, sales, … - All forms look the same. To not to style and have all forms look the same you need a CSS plugin to add contact form 7 css per page. - Place two forms with different styles in one page/post. - Set a background image and align it on the right top angle. - Needs a bunch of CSS knowledge to do it. Do you have it? - Style a form which is inside a page builder as the builder could stripe out all your CF7 form CSS == Why Choose CF7 WOW Styler? == The CF7 WOW Styler has been developed for those users who have already activated and used Contact Form 7 and do not know how to style and design the created forms without CSS knowledge. This plugin enables to use a default template and create a new design from scratch with 73 style settings. As well reuse style and alter it for other forms. The plugin makes the form design process even easier than you might think. You can style your forms on one page or post, style even a form inside a page builder, create multiple style schemes and have multiple different styled forms on one page, etc. And this is a matter of a few clicks with CF7 WOW Styler. Moreover, with the help of Live preview, you will have an opportunity to make the needed adjustments to the form design by viewing it at the same time. Also, the Split view will enable you to see your form with a live style (published) and style in progress (draft) simultaneously. Its full compatibility with Wide Themes, page builders, and browsers, ease of use, and time-saving features make WOW Styler a great way to create a unique style and design for your website forms created with the WordPress Contact Form 7 plugin. The best from default WOW Styler try to take your theme style like font, colors as default and you just add what's needed. The WOW Styler plugin is all about simplicity and great form styling features both for non-tech users and those who have advanced programming and coding skills. == How to Get Started with WOW Styler Free? == Getting started with WOW Styler is a matter of a few seconds. So, take these steps to easily get started with the WOW Styler Free Version: 1. Inside your WordPress backend, admin area go to “Plugins” > “Add new plugin” 2. Search for "wow styler" in the WP Plugins Repository 3. Install and activate the WOW Styler 4. Enjoy some of the plugin’s features for free. == CF7 WOW Styler Free Features == WOW, Styler’s free version gives you an opportunity to create a basic design for your contact forms. It includes the following customization options: - Text style customization - Text color - Font size - 6 font family selection - Links color - Links hover color - Labels color - Labels font size - Font weight - Font style (Normal, Italic, Oblique, Inherit) - Form padding, margin, and border - Field input - Buttons with text, background, hover, hover background color, font size, button, and shadow styles customization - Style scheme preview - Split view - Second Column view (Live, Unstyled) - Duplicate form in the second column - Default style scheme for all forms - Implementing custom CSS for tech-savvy users The WOW Styler eases the user experience by using also the theme styles as a base. This helps users save time to get the theme style into the CF7 form style with the WOW styler. To make your experience smoother and more pleasant the plugin is always under improvement. In case of any issues, the users are welcome to get high-quality support from the Customer Satisfaction specialists. == Quick Used Cases of the Free Version == You can greatly benefit from some of the best features of the WOW Styler plugin absolutely for free. The users who have built their contact forms through the Contact Form 7 plugin can use the WOW Styler in multiple ways, such as: - Changing colors (including text, background, hover) of the default form scheme to keep your brand identity. - Changing only a background color or a hover color and saving the form with different schemes. - Use buttons with different style options to attract more attention to them. - Use the Input Fields options to style your custom inputs in a unique way. == How to Switch to the Pro version? == If you have an active WP2LEADS Pro version (Plugin connects WordPress with KlickTipp german email marketing) then you can use the WOW Styler with all its Premium features absolutely for free. Or else, you can enjoy all the settings of the plugin in a 14-day free trial period. Days before finishing the trial period, you will get an Email reminder that you will be auto upgraded to the Pro version you choose which is most suitable plans for your usage. Here's how to switch to the Pro version: 1. Find the CF7 Styler in the “Contact” submenu. 2. Move to the “Upgrade” section. 3. Choose your desired Pro package and make a purchase directly from there. 4. License will be activated automatically. 5. The logged-in user email is used to send a copy of your License key, premium download link, and invoice. == CF7 WOW Styler Pro Features == The CF7 WOW Styler Pro version offers additional features and customization options. It includes all the features available in the Free version, plus the following: - Buttons (radio buttons and checkboxes) and style boxes one per line (instead of one after the other without a new line) - Individual styles for forms - Multiple style schemes to switch and attach to Contact Form 7 forms - Background picture and gif, opacity - Loading styles in tag in case the theme or a page builder removes the “Custom CSS” function that adds the CF7 Style. == Brilliant Used Cases of the WOW Styler Pro == If you want to make your Contact form 7 form more unique, live, and engaging, then you can implement gifs to your form background. In case your page builder removes styles loaded in the header and your form remains unstyled, you can load style in the body to show your form’s attractive design.
    Please make sure you read the latest changes and benefits from our WP.org changelog to get the most out of the CF7 styling plugin.
    Start by using the New Handbook & Knowledge Base! == Frequently Asked Questions == Here are some frequently asked questions about the WOW Styler plugin: = Can I test the Pro features in a free trial period? = Yes, all the Pro features are available in the 14 days’ free trial period. During that time, you will get an Email notification reminding you to get auto-upgraded to the Premium version to continue using the WOW Styler Pro features. = How can I get the WOW Styler Pro for free? = If you have activated WP2LEADS Pro license, then you can use the WOW Styler Pro version for free. Referee paying users and pay your license fee with the 35% affiliation commission. Or if you use our affiliation program referee 3 get your license "free". 3x35% = 105%. Join the program inside the plugin after opt-in. = Can I propose a plugin customization? = We are very happy to receive individual customization proposals. Just create a screencast video, avoiding showing any personal data. Share the video link via the support chat in the support forum. You will get the solution to your issue mostly in just one day. = Do you have an Affiliate program? = Yes, we have an Affiliate program, so you can earn money with the CF7 WOW Styler. You will get a 35% lifetime commission through your affiliate link and even more by writing a blog post. In this case, you will also obtain 12 months pro version. You can find the program details by entering the “Affiliation” section available directly inside the plugin. Watch the video to get a lifetime income with the WOW Styler Affiliate program. https://www.youtube.com/embed/F07oKoZ1xpc * More sources you could also use: https://www.youtube.com/watch?v=1eMYnhXzTaU https://ps.w.org/cf7-styler/assets/screenshot-1.gif Animations that show the difference unstyled Contact Form 7 and WOW Styler styled CF7 forms: https://saleswonder.biz/wp-content/uploads/wow-styler-for-cf7-banner-anination-800px.webp https://saleswonder.biz/wp-content/uploads/wow-styler-for-cf7-banner-anination.webp PS: You can use all media and text you can find and we created to affiliate marketing the plugin * Blog post draft for you to use: Do you know already the Wordpress Plugin "WOW Style for Contact form 7"? https://saleswonder.biz/wp-content/uploads/wow-styler-for-cf7-banner-anination.webp * You can style Contact Form 7 forms, like changing colors, shapes, shadows, backgrounds, buttons, … without any knowledge of CSS. * Animated tutorial gifs, get faster styled * Here is the link to the free Plugin https://wordpress.org/plugins/cf7-styler/ = What if I cannot find the Affiliate program? = If you cannot find an affiliate program and you have a Pro license activated through WP2LEADS, then you should deactivate it, become an affiliate, then activate WP2LEADS once more. = What are the server requirements to use the WOW Styler? = The minimum PHP memory must be 64MB. 128MB will be better. The higher your PHP memory, the slower you will run to the white site. = What if I use Thrive Architect? = Here is a video tutorial on how to add the CF7 Wow Styler in Thrive Architect page builder. [See tutorial here](https://design-for-contact-form-7-wow-styler.tawk.help/article/how-to-add-cf7-style-in-thrive-architect ) = Show me what can i style with the Wow Styler?
    • Form Text Text Color Font Size Font Family Links Settings Links Color Links Hover Color Labels Settings Labels Color Labels Font Size Labels Font Weight Labels Font Style
    • Form Background Image & Colors Background Color Background Image (Premium) Upload Image Image Opacity Image Size like cover, contain, repeat vertical, repeat horizontal, repeat both Image Position like up, down, left, right and every corner
    • Form Padding, Margin & Border Padding Margin Border Width Border Radius Border Color Form Shadow Horizontal Length Vertical Length Blur radius Spread radius Shadow Color Opacity Shadow Position
    • Input Fields Text and Colors Make input fields full width? YES/NO Font Size Line Height Text Color BG Color BG Opacity
    • Input Fields Padding & Margin Padding Margin Border Radius Color Input Shadow Horizontal Length Vertical Length Blur radius Spread radius Shadow Color Opacity Shadow Position
    • Checkboxes & Radiobuttons Make checkbox item one per line YES/NO (Premium) Make radio button item one per line YES/NO (Premium) Labels Font Size
    • Button Size, Text & Colors Make full width? YES/NO Padding Text Color Hover Text Color BG Color Hover BG Color Font Size Line Height
    • Button Border & Shadow Button Border Width Radius Color Hover Color Button Shadow Horizontal Length Vertical Length Blur radius Spread radius Shadow Color Shadow Opacity Shadow Position
    • Custom CSS clear, preview button
    • Style headers
    • Add image to form
    • Contact Form 7 fields two columns
    = How can I translate the plugin? = You can use the Loco Translate WordPress plugin by generating .po and .mo files. After that, you should share the generated files via this email address: support@saleswonder.biz. = You have a better, additional translation? Great ;-) Premium one year free as reward = Use https://wordpress.org/plugins/loco-translate/ to generate a .po and .mo file and share files via support"at"saleswonder.biz thanks. = How can I update, handle the newly purchased Pro license? = [Handle license, free version here](https://users.freemius.com/login) * If you want to buy license do directly inside the plugin, so the license get activated directly * If bought go to the confirmation email from Freemius and download and install the Premium version. Free version will be deactivated. On activation opt-in and enter your key, you also got in your email. Have fun pro style your Contact Form 7 forms ;-) = How can I report security bugs? = You can report security bugs through the Patchstack Vulnerability Disclosure Program. The Patchstack team help validate, triage and handle any security vulnerabilities. [Report a security vulnerability.](https://patchstack.com/database/vdp/cf7-styler) == Installation == Let's get your forms styled * Step 1: Install/activate Contact Form 7 WOW style plugin * Step 2: Go to CF7 Styler which is in the "Contact" sub menu **Pst. If you like some example forms which are already connected to [Klick Tipp an German E-Mail and SMS Marketing Provider] (https://www.klick-tipp.com/15194) [Get WP2LEADS too](https://de.wordpress.org/plugins/wp2leads/). If you are using WP2LEADS with an active Pro Version the Contact Form 7 Styler Premium version is free for you. == Screenshots == 1. How to add style in contact form 7 to meet your sites CI and design? 2. Make radio button items one per line and checkbox items one per line 3. How to add Contact Form 7 background images 4. Lets style Contact Form 7 5. Before - unstyled contactform7 6. Please rate us - form with styler interface with Contact Form 7 style 7. Example how to add style in Contact Form 7 with Sunflower form backgrounds 8. GIF what is new with version == Upgrade Notice == = All upgrades are compatible when upgrade from any Contact Form 7 style plugin version = The latest version is checked during testing update on our site and will work. == Changelog == = 1.7.0 2025.01.03 = = Successful 2025 = = Frontend security issue - all update please = * Security fix thanks for reporting and fixing * Freemius SDK update, which includes multiple bug fixes and improvements. update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases * For nice CF7 extensions please checkout https://add-ons.org/plugin/contact-form-7-add-on-bundle-all-in-one/ = 1.6.9 2024.11.19 = * Security fix thanks for reporting and fixing * Freemius SDK update, which includes multiple bug fixes and improvements. update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases Benefit: Better opt-in * WP 6.7 compatible * For nice CF7 extensions please checkout https://add-ons.org/plugin/contact-form-7-add-on-bundle-all-in-one/ = 1.6.8 2024.09.14 = * Freemius SDK update, which includes multiple bug fixes and improvements. update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases Benefit: Better opt-in * WP 6.7 compatible * For nice CF7 extensions please checkout https://add-ons.org/plugin/contact-form-7-add-on-bundle-all-in-one/ = 1.6.7 2024.07.06 = * fix polyfill issue, thanks to WP.org plugin team * Freemius SDK update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases Benefit: Better opt-in * WP 6.6 compatible * For nice CF7 extensions please checkout https://add-ons.org/plugin/contact-form-7-add-on-bundle-all-in-one/ = 1.6.6 2024.06.20 = * Updated Freemius * All working good = 1.6.5 2024.03.09 = * Improved security thanks patch stack = 1.6.4 2024.01.03 = = Beautiful styled 2024 = * Freemius SDK update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases Benefit: Reduce the data saved in database, by cleaning up. = 1.6.3 2023.12.19 = * Freemius SDK update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases Benefit: Better opt-in, trial and license handling = 1.6.2 2023.11.23 = * Freemius SDK update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases Benefit: Better opt-in, trial and license handling * Sorry, I forgot to also upload the new version to the premium user now i did What feature you like to see? Write here in chat here. = 1.6.1 2023.11.02 = * Freemius SDK update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases Benefit: Better opt-in, trial and license handling What feature you like to see? Write here in chat here. = 1.6.0 2023.10.27 = * Feature: Support CF7 form styling for new and old form ID on Gutenberg editor * Freemius SDK update for better opt-in for information via email, license handling (free, trial, paid) https://github.com/Freemius/wordpress-sdk/releases = 1.5.5+1.5.6 2023.09.01 = * Freemius SDK update https://github.com/Freemius/wordpress-sdk/releases * Your forms are styled again (working with ID and hash attribute) Issue was the CF7 5.8 update that introduced: Introduces SHA-1 hash-based contact form identification. AND Ignores the id attribute if the same ID has been used for another element. https://contactform7.com/2023/08/06/contact-form-7-58/ * WP 6.3 working = 1.5.4 2023.07.14 = * SDK update = 1.5.2 + 1.5.3 released 2023.06.15 = * Updated: Freemius SDK for better license, trial, opt-in handling Please all update = 1.5.0 + 1.5.1 released 2023.04.15 = * Fix: Avoid fatal error when activate the plugin twice When upgrade free to Pro free gets deactivated. * Fix: Freemius SDK update fixes one error * Added/updated: Languages total: IT (Italian), VI (Vietnamese) HI (Hindi) ES-MX Spanish (Mexico) UR (Urdu) RU (Russian) PT-BR Portuguese (Brazil), DE German PO Polish ES Spanish (Spain) FRFrench (France) JA Japanese VI Vietnamese Thank you for your trust and great support by buying the Pro version read more in the full changelog.txtLICENSE.txt000064400000043253147600046700006402 0ustar00 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.index.php000064400000000032147600046700006363 0ustar00Start and improve your forms by using the New Handbook & Knowledge Base articles! Please on the page select "T" or "NT" user to see the articles. = 1.4.3 released 2022.09.11 = * Added: Knowledge base & chat/ticket support inside the plugin * Added: Four knowledge base articles Start and improve your forms by using the New Handbook & Knowledge Base articles! Please on the page select "T" or "NT" user to see the articles. = 1.4.2 released 2022.02.24 = * Security fix: in one of our used libraries all update please! * More easy license and trial handling. Benefits: Trial for free is always the best start to have multiple forms each styled different with nice light background images ect. = 1.4.1 released 2021.03.09 = * Compatible with the latest CF7 and WP Version * Updated Freemius SDK which handles trial, paid versions and opt-in to exclusive email tutorials = 1.4.0 released 2020.12.04 = * updated to the latest freemius SDK for more security, privacy and usability * added menu item 'Opt-in to see account' if admin skip opt-in previously * feel free to request more features = 1.3.13 released 2019.08.25 = * Checked and WP 5.5 compatible * Thank you all very much for 1000 active installations * To thank you please handover your cf7 style needs and we will look what we can do ;-) = 1.3.12 released 2019.07.27 = * updated the tutorial page inside the plugin:
    • Tutorial content:
    • Simple style
    • Complex style
    • Style headings
    • Add image to form
    • Create 2 columns
    Your benefits you can do more with your forms. = 1.3.11 released 2019.07.21 = * Contact form 7 style headings with the WOW Styler please watch the tutorial video and use the CSS code, both provided inside the tutorials page in the plugin = 1.3.10 released 2019.05.28 = * New and short videos and images inside the plugin in the tutorial section Your benefits easy and fast start = 1.3.9 released 2019.04.18 = * First step to more functionality * Updated all translations = 1.3.8 released 2019.04.07 = * Hand translated french, spanish and polish translation replace the machine translation. Thank you Marek from translation service http://www.lexpertise.eu/. For each translation he got an one site lifetime version in exchange. * This deal i do with every hand translated main language which is sent to support@saleswonder.biz. Easy translation with free "loco" translation plugin. * Roadmap: We will add a lot more styling features. Your benefit would be that you can style any part of the form. * We where busy developing the "Better reviews for WooCommerce", we developed for ourself and now released new version for all. https://wordpress.org/plugins/more-better-reviews-for-woocommerce/ = 1.3.7.2 released 2019.02.06 = * Added translations (by Yandex): Portuguese (Brazil), Polish, Spanish (Spain), French (France), Japanese, Russian * Already translated by humans in English (US) and German (all kinds) Benefits of the translations: You feel more home, can work faster and can use this plugin if you do not unterstand english. * You have a better, additional translation? Great ;-) Use https://wordpress.org/plugins/loco-translate/ to generate a .po and .mo file and share files via support@saleswonder.biz = 1.3.7.1 released 2019.01.18 = * Added: Buttons to the tutorial and welcome page Benefits: Faster access to the styler = 1.3.7 released 2019.01.17 = * Added: 3 Animated gifs, to the wp.org screenshots section and to the tutorial page inside the plugin Benefit: Match the form to your style, add background image, get buttons aligned in seconds * Added: New shorter intro video Benefit: Get faster how the plugin works * Updated: Plugin name Benefit: One name allover "WOW Style Contact Form 7" for easy search and handling = 1.3.6 released 2019.01.10 = * 2 Fixes and 1 small known issue * Known Pro version issue: When you add a background and label color to a scheme and activate scheme for all forms, your individual styled forms are styled with the colors too. Workaround: Click "Disable style for all forms" and Activate style one by one per form, until fixed ;-), sorry. * Fixed: Change permalink handling to show form if permalink not set up Benefit: In every permalink setting now the styles previews the form (Thank you Vlad, Brian) * Fixed: Link above each form in frontend Benefit: With more than one form on a page or post you can now click on edit button above each form, for instant editing (Thank you Vlad) = 1.3.5 released 2019.01.07 = * Known Bug: Permalink Settings When "Common Settings" set to "Plain" like "http://domain.com/?p=123" Results in an 404 in the styler form preview Workaround: Please choose a different setting to "Plain" Issue sent to developer Bug will be fixed. * Minimum PHP memory 64 M better 128 M Example: When using a lot of memory consuming plugins and you activate styler you could run out of memory. This is not only happen with CF7 style plugin but also common with other plugins. Task to check memory consumption is on the developers task list. Benefits of a higher PHP Memory: When install and activate plugins you not run into white site. * Welcome and Tutorial video updated now you see the latest styler version in action Benefits: See how to style your forms in minutes into an eye catching form with a higher conversion rate. * Updated screenshots Benefits: You see better how to add style in Contact Form 7 with beautiful backgrounds by looking at the beautiful examples. = 1.3.4 released 2019.12.28 = I am really sorry the admin bar was not shown. I got aware of it by using the plugin myself. Now tested and working ;-) * Fix: Show admin bar on frontend Your benefit: Have your frontend admin bar, to access backend easily, back So if you running into trouble please contact us and we will solve issue. Also feature requests are welcome. = 1.3.2 - 1.3.3 released 2019.12.19 = * Split view also in free version Your benefit: Always see style changes instantly Your benefit: Always see live form style * Show duplicated form in second column Your benefit: Better comparison of design view and live/unstyled view * Reduced menu height on the right side Your benefit: More space for styling = 1.3.1 released 2019.12.17 = * Fix: Make switch to scroll or fix inside split mode = 1.3 released 2019.12.17 = * Added: Split view Benefit: See live results when styling or saving style. Best for usability. = 1.2.6 - 1.2.7 released 2019.12.03 = * Tip: Opt-in with freemius to get access to extrem long trial period version. Your benefit is you can test everything for free without entering any payment infos. * Tutorial and welcome page with quick start & quick style video Your benefit is an easy start with the styler, i had fun doing this video. * Fix an issue when showed wrong style scheme on page loading Your benefit is an more intuitive style flow * Fix an font family issue seen on Twenty Twenty theme Now fonts from styler overwrite theme fonts in form = 1.1.9 - 1.2.5 released 2019.11.15 = * Improved german translation by Melanie and Tobias * Added: border style settings * Added: label font weight and font style * Added: Style mode current/live/unstyled dropdown * Added: link from frontend to styler Benefit: You open a form click on "Open Styler" and edit the style * Added: CF7 editor added dropdown to choose style * Added: Added clear, preview button to custom CSS Code Benefit: Where your create your form you can choose style scheme too. * Moved: some functions to premium Benefit: You get a CF7 Styler that is supported and working continuously * Moved: some settings together to reduce clicking * Fixed: Cf7_Quiet_Skin declaration in WP 5.3 * Fixed: Custom CSS-Code area * Fixed: Scrolling issue = 1.1.3 - 1.1.8 released 2019.11.09 = * Stunning Background image settings like Background Color behind picture, Picture Opacity, Image Size like cover, contain, repeat vertical, repeat horizontal, repeat both Image position * Added setting to load styles in body-tag instead of head-tag (Lifetime Premium or WP2LEADS Pro active) Case: Themes or page builder plugins can stripe out CSS from head-tag and leave your form unstyled. Benefit: 1) Activate this option per page (directly on page near the form) or globally for all forms to get your styles back 2) Tested Themes and page builder compatibility: Thrive Architect, Optimize Press, Divi Theme, Elementor * Added German translation (Thank you, Melanie and Tobias) * Improved saving buttons usability, by conditionally grey out some buttons Benefit: You see intuitively what are your next options * Disable freemius if wp2leads is pro Case: We sell via freemius and we allow to use the premium version for free for all WP2LEADS Pro license holder Benefit: freemius will not be shown to you, when you are a WP2LEADS Pro license holder * Show sticky message instead of popup alert Benefit: You do not have to confirm saving ect. * Removed knowledge base link and added all infos to plugin * Updated Message with link if CF7 plugin got deactivated on plugins page = 1.1.1 - 1.1.2 released 2019.11.06 = After a lot of testing we are proud to release our first cf7 styler version on wp.org * Added knowledge base link message * stay fullscreen after delete color scheme * Sanitise values * Fixed Quiet_Skin error * added Padding to default style scheme * uncheck unstyle by default * fix mobile and tab view height = 1.1.0 released 2019.11.04 = * Install Contact Form 7 (CF7) if not yet installed, because it is a required plugin. * Padding for form preview * uncheck unstyle by default * fix mobile and tab view height * Sanitize values * Changed slug and title = 1.0 released 2019.11.01 =cf7-styler.php000064400000020027147600046700007261 0ustar00 '4879', 'slug' => 'cf7-styler', 'premium_slug' => 'cf7-styler-pro', 'type' => 'plugin', 'public_key' => 'pk_430f963531baceba1e271f3a35041', 'is_premium' => false, 'premium_suffix' => 'Pro', 'has_premium_version' => false, 'has_addons' => false, 'has_paid_plans' => true, 'is_org_compliant' => true, 'trial' => array( 'days' => 14, 'is_require_payment' => true, ), 'has_affiliation' => 'all', 'menu' => array( 'slug' => 'cf7cstmzr_page', 'support' => true, 'contact' => false, 'parent' => array( 'slug' => 'wpcf7', ), ), 'secret_key' => $secret_key, ); if ( !cf7cstmzr_is_plugin_activated( 'contact-form-7', 'wp-contact-form-7.php' ) ) { $params['menu'] = array( 'slug' => 'cf7cstmzr_page', 'support' => true, 'contact' => false, ); } $cf7_styler = fs_dynamic_init( $params ); } return $cf7_styler; } // Init Freemius. cf7_styler(); // Signal that SDK was initiated. do_action( 'cf7_styler_loaded' ); } } if ( !function_exists( 'cf7cstmzr_show_cf7_missing_notice' ) ) { function cf7cstmzr_show_cf7_missing_notice() { echo '

    ' . sprintf( esc_html__( 'CF7 Customizer requires Contact Form 7 plugin to be installed and active. You can download %s.', 'cf7-styler' ), 'Contact Form 7 here' ) . '

    ' ; } } if ( !function_exists( 'cf7cstmzr_show_dev_env_notice' ) ) { function cf7cstmzr_show_dev_env_notice() { ?>

    DEV env: version current branch

    run(); } } run_cf7_customizer();