commit 7464e10f35dd1cb0b59ccf3577ea7042ffafeb0d Author: Prospect Ogujiuba Date: Tue Jul 2 11:00:03 2024 -0400 first commit diff --git a/404.php b/404.php new file mode 100644 index 0000000..4b38782 --- /dev/null +++ b/404.php @@ -0,0 +1,21 @@ + + +
+
+
+
+
harplabs logo +
+
+

+

+

+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/assets/css/styles.css b/assets/css/styles.css new file mode 100644 index 0000000..a0c7118 --- /dev/null +++ b/assets/css/styles.css @@ -0,0 +1,2676 @@ +@charset "UTF-8"; +@import url("https://fonts.googleapis.com/css?family=Montserrat:100,200,300,400,500,600,700,800,900|Inter:100,200,300,400,500,600,700,800,900&display=swap"); +:root { + /* Red */ + --red-50: #f6d9d9; + --red-100: #f3b5ab; + --red-200: #eb9280; + --red-300: #e06e56; + --red-400: #d2462d; + --red-500: #c00000; + --red-600: #9f0004; + --red-700: #7f0005; + --red-800: #600004; + --red-900: #430000; + /* Green */ + --green-50: #ecfdf5; + --green-100: #d1fae5; + --green-200: #a7f3d0; + --green-300: #6ee7b7; + --green-400: #34d399; + --green-500: #10b981; + --green-600: #059669; + --green-700: #047857; + --green-800: #065f46; + --green-900: #064e3b; + /* Primary */ + --primary-50: #dadfe3; + --primary-100: #afb6c1; + --primary-200: #858fa0; + --primary-300: #5d6a80; + --primary-400: #364761; + --primary-500: #0a2744; + --primary-600: #0b2138; + --primary-700: #0b1b2d; + --primary-800: #091522; + --primary-900: #040e18; + /* Secondary */ + --secondary-50: #fff7e0; + --secondary-100: #ffefc4; + --secondary-200: #ffe7a5; + --secondary-300: #ffde85; + --secondary-400: #ffd561; + --secondary-500: #ffcc33; + --secondary-600: #d3a92b; + --secondary-700: #a88722; + --secondary-800: #80661a; + --secondary-900: #594712; + /* Gray */ + --gray-50: rgba(249, 250, 251, 1); + --gray-100: rgba(243, 244, 246, 1); + --gray-200: rgba(229, 231, 235, 1); + --gray-300: rgba(209, 213, 219, 1); + --gray-400: rgba(156, 163, 175, 1); + --gray-500: rgba(107, 114, 128, 1); + --gray-600: rgba(75, 85, 99, 1); + --gray-700: rgba(55, 65, 81, 1); + --gray-800: rgba(31, 41, 55, 1); + --gray-900: rgba(17, 24, 39, 1); + /* Black & White */ + --black: #000; + --white: #fff; +} +/* Box sizing rules */ +*, +*::before, +*::after { + box-sizing: border-box; +} +* { + margin: 0; + padding: 0; + font: inherit; +} +/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */ +ul[role=list], +ol[role=list] { + list-style: none; +} +/* Set core root defaults */ +html:focus-within { + scroll-behavior: smooth; +} +html, +body { + height: 100%; + scroll-behavior: smooth; +} +/* Set core body defaults */ +body { + text-rendering: optimizeSpeed; + line-height: 1.5; +} +/* A elements that don't have a class get default styles */ +a:not([class]) { + -webkit-text-decoration-skip: ink; + text-decoration-skip-ink: auto; +} +/* Make images easier to work with */ +img, +picture, +svg { + max-width: 100%; + display: block; +} +/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */ +@media (prefers-reduced-motion: reduce) { + html:focus-within { + scroll-behavior: auto; + } + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} +/* FORMS */ +input[type=checkbox]:focus, +input[type=color]:focus, +input[type=date]:focus, +input[type=datetime-local]:focus, +input[type=datetime]:focus, +input[type=email]:focus, +input[type=month]:focus, +input[type=number]:focus, +input[type=password]:focus, +input[type=radio]:focus, +input[type=search]:focus, +input[type=tel]:focus, +input[type=text]:focus, +input[type=textarea]:focus, +input[type=time]:focus, +input[type=url]:focus, +input[type=week]:focus, +select:focus, +textarea:focus { + border-color: #0a2744 !important; + border-color: var(--primary-500) !important; + box-shadow: 0 0 0 1px rgba(107, 114, 128, 1) !important; + box-shadow: 0 0 0 1px var(--gray-500) !important; + outline: 2px solid transparent !important; +} +[type=text], +[type=email], +[type=url], +[type=password], +[type=number], +[type=date], +[type=datetime-local], +[type=month], +[type=search], +[type=tel], +[type=time], +[type=week], +[multiple], +textarea, +select { + width: 100% !important; +} +textarea, +textarea:focus, +textarea:focus-within { + border-color: #040e18 !important; + border-color: var(--primary-900) !important; +} +/* WordPress */ +/* Archive Widget */ +/* Latest Posts Featured Image */ +.wp-block-latest-posts__featured-image img { + border-radius: 0.5rem; +} +/* Latest Posts Title */ +.wp-block-latest-posts__post-title { + font-weight: 600; +} +/* Login Page */ +#login { + display: flex; + flex-direction: column; + align-items: center; + padding: 0; +} +.login #nav { + font-size: 16px; + font-weight: 400; +} +.login #nav a, +.login #backtoblog a { + color: #fff; + color: var(--white); +} +.login #backtoblog a:hover, +.login #nav a:hover, +.login h1 a:hover { + color: #858fa0; + color: var(--primary-200); +} +.login .button.wp-hide-pw { + color: #000; + color: var(--black); +} +.login .button.wp-hide-pw:hover { + color: #0a2744; + color: var(--primary-500); +} +.login .button.wp-hide-pw:focus { + background: 0 0; + border-color: #040e18; + border-color: var(--primary-900); + box-shadow: 0 0 0 1px; + outline: 2px solid transparent; +} +#login h1 a, +.login h1 a { + background-image: url("../../assets/images/logos/Leon-Pharmacy-Logo-whitebg.png"); + background-repeat: no-repeat; + background-position: center center; + background-size: 200px; + width: 200px; + height: 84px; + margin: 0px 0px 0.5rem; +} +.login form { + border-radius: 8px; +} +.login .message { + border-left: 8px solid #0a2744; + border-left: 8px solid var(--primary-500); +} +/* Customize Login Screen */ +body.login .button-primary { + font-size: 100%; + width: 100%; + margin: 0.5rem 0 0; + line-height: 1; + cursor: pointer; + position: relative; + -webkit-text-decoration: none; + text-decoration: none; + overflow: visible; + padding: 0.75rem 1.25rem; + font-weight: 400; + left: auto; + color: #fff; + color: var(--white); + background-color: #0a2744; + background-color: var(--primary-500); + border: 0; + display: inline-block; + background-image: none; + box-shadow: none; + text-shadow: none; + border-radius: 0.375rem; + transition: all 600ms ease-in-out; +} +body.login .button-primary:active, +body.login .button-primary:focus, +body.login .button-primary:hover { + background-color: #fff; + background-color: var(--white); + -webkit-text-decoration: none; + text-decoration: none; + background-image: none; + color: #000; + color: var(--black); + border-radius: 0.375rem; + border: 1px solid rgba(17, 24, 39, 1); + border: 1px solid var(--gray-900); +} +body.login { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-size: cover; + background-position: center center; + background-image: url("../../assets/images/bgs/leon-pharmacy-entrance.jpg"); + background-color: rgba(31, 41, 55, 1); + background-color: var(--gray-800); + background-blend-mode: multiply; +} +.login-action-confirm_admin_email #login { + margin-top: auto; +} +/* Pinegrow generated Design Panel Init Begin */ +/* Pinegrow generated Design Panel Init End */ +/* ! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com */ +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} +::before, +::after { + --tw-content: ""; +} +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +*/ +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: "Montserrat", sans-serif; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ +} +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline; + text-decoration: underline dotted; +} +/* +Remove the default font size and weight for headings. +*/ +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ +a { + color: inherit; + text-decoration: inherit; +} +/* +Add the correct font weight in Edge and Safari. +*/ +b, +strong { + font-weight: bolder; +} +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} +/* +Add the correct font size in all browsers. +*/ +small { + font-size: 80%; +} +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sub { + bottom: -0.25em; +} +sup { + top: -0.5em; +} +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ +button, +select { + text-transform: none; +} +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ +button, +[type=button], +[type=reset], +[type=submit] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} +/* +Use the modern Firefox focus style for all focusable elements. +*/ +:-moz-focusring { + outline: auto; +} +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ +:-moz-ui-invalid { + box-shadow: none; +} +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ +progress { + vertical-align: baseline; +} +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ +[type=search] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ +::-webkit-search-decoration { + -webkit-appearance: none; +} +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} +/* +Add the correct display in Chrome and Safari. +*/ +summary { + display: list-item; +} +/* +Removes the default spacing and border for appropriate elements. +*/ +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} +fieldset { + margin: 0; + padding: 0; +} +legend { + padding: 0; +} +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} +/* +Reset default styling for dialogs. +*/ +dialog { + padding: 0; +} +/* +Prevent resizing textareas horizontally by default. +*/ +textarea { + resize: vertical; +} +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} +/* +Set the default cursor for buttons. +*/ +button, +[role=button] { + cursor: pointer; +} +/* +Make sure disabled buttons don't get the pointer cursor. +*/ +:disabled { + cursor: default; +} +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ +img, +video { + max-width: 100%; + height: auto; +} +/* Make elements with the HTML hidden attribute stay hidden by default */ +[hidden] { + display: none; +} +[type=text], input:where(:not([type])), [type=email], [type=url], [type=password], [type=number], [type=date], [type=datetime-local], [type=month], [type=search], [type=tel], [type=time], [type=week], [multiple], textarea, select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 rgba(0,0,0,0); +} +[type=text]:focus, input:where(:not([type])):focus, [type=email]:focus, [type=url]:focus, [type=password]:focus, [type=number]:focus, [type=date]:focus, [type=datetime-local]:focus, [type=month]:focus, [type=search]:focus, [type=tel]:focus, [type=time]:focus, [type=week]:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #0b2138; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-empty, ) 0 0 0 0px #fff, var(--tw-empty, ) 0 0 0 calc(1px + 0px) #0b2138, var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #0b2138; +} +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} +input::placeholder, textarea::placeholder { + color: #6b7280; + opacity: 1; +} +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} +::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} +::-webkit-datetime-edit { + display: inline-flex; +} +::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} +[multiple], [size]:where(select:not([size="1"])) { + background-image: none; + background-image: initial; + background-position: 0 0; + background-position: initial; + background-repeat: repeat; + background-repeat: initial; + background-size: auto auto; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: inherit; +} +[type=checkbox], [type=radio] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #0b2138; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 rgba(0,0,0,0); +} +[type=checkbox] { + border-radius: 0px; +} +[type=radio] { + border-radius: 100%; +} +[type=checkbox]:focus, [type=radio]:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #0b2138; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-empty, ) 0 0 0 2px #fff, var(--tw-empty, ) 0 0 0 calc(2px + 2px) #0b2138, var(--tw-shadow); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} +[type=checkbox]:checked, [type=radio]:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} +[type=checkbox]:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} +[type=radio]:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} +[type=checkbox]:checked:hover, [type=checkbox]:checked:focus, [type=radio]:checked:hover, [type=radio]:checked:focus { + border-color: transparent; + background-color: currentColor; +} +[type=checkbox]:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} +[type=checkbox]:indeterminate:hover, [type=checkbox]:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} +[type=file] { + background: transparent none repeat 0 0 / auto auto padding-box border-box scroll; + background: initial; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: inherit; + line-height: inherit; +} +[type=file]:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(10, 39, 68, 0.5); + --tw-ring-offset-shadow: 0 0 rgba(0,0,0,0); + --tw-ring-shadow: 0 0 rgba(0,0,0,0); + --tw-shadow: 0 0 rgba(0,0,0,0); + --tw-shadow-colored: 0 0 rgba(0,0,0,0); + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(10, 39, 68, 0.5); + --tw-ring-offset-shadow: 0 0 rgba(0,0,0,0); + --tw-ring-shadow: 0 0 rgba(0,0,0,0); + --tw-shadow: 0 0 rgba(0,0,0,0); + --tw-shadow-colored: 0 0 rgba(0,0,0,0); + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} +.container { + width: 100%; +} +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} +.prose { + color: var(--tw-prose-body); + max-width: 65ch; +} +.prose :where(p):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} +.prose :where([class~=lead]):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-lead); + font-size: 1.25em; + line-height: 1.6; + margin-top: 1.2em; + margin-bottom: 1.2em; +} +.prose :where(a):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-links); + -webkit-text-decoration: underline; + text-decoration: underline; + font-weight: 500; +} +.prose :where(strong):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-bold); + font-weight: 600; +} +.prose :where(a strong):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; +} +.prose :where(blockquote strong):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; +} +.prose :where(thead th strong):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; +} +.prose :where(ol):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: decimal; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-left: 1.625em; +} +.prose :where(ol[type=A]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: upper-alpha; +} +.prose :where(ol[type=a]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: lower-alpha; +} +.prose :where(ol[type=A s]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: upper-alpha; +} +.prose :where(ol[type=a s]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: lower-alpha; +} +.prose :where(ol[type=I]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: upper-roman; +} +.prose :where(ol[type=i]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: lower-roman; +} +.prose :where(ol[type=I s]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: upper-roman; +} +.prose :where(ol[type=i s]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: lower-roman; +} +.prose :where(ol[type="1"]):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: decimal; +} +.prose :where(ul):not(:where([class~=not-prose], [class~=not-prose] *)) { + list-style-type: disc; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-left: 1.625em; +} +.prose :where(ol > li):not(:where([class~=not-prose], [class~=not-prose] *))::marker { + font-weight: 400; + color: var(--tw-prose-counters); +} +.prose :where(ul > li):not(:where([class~=not-prose], [class~=not-prose] *))::marker { + color: var(--tw-prose-bullets); +} +.prose :where(dt):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.25em; +} +.prose :where(hr):not(:where([class~=not-prose], [class~=not-prose] *)) { + border-color: var(--tw-prose-hr); + border-top-width: 1px; + margin-top: 3em; + margin-bottom: 3em; +} +.prose :where(blockquote):not(:where([class~=not-prose], [class~=not-prose] *)) { + font-weight: 500; + font-style: italic; + color: var(--tw-prose-quotes); + border-left-width: 0.25rem; + border-left-color: var(--tw-prose-quote-borders); + quotes: "“" "”" "‘" "’"; + margin-top: 1.6em; + margin-bottom: 1.6em; + padding-left: 1em; +} +.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose], [class~=not-prose] *))::before { + content: open-quote; +} +.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose], [class~=not-prose] *))::after { + content: close-quote; +} +.prose :where(h1):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-headings); + font-weight: 800; + font-size: 2.25em; + margin-top: 0; + margin-bottom: 0.8888889em; + line-height: 1.1111111; +} +.prose :where(h1 strong):not(:where([class~=not-prose], [class~=not-prose] *)) { + font-weight: 900; + color: inherit; +} +.prose :where(h2):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-headings); + font-weight: 700; + font-size: 1.5em; + margin-top: 2em; + margin-bottom: 1em; + line-height: 1.3333333; +} +.prose :where(h2 strong):not(:where([class~=not-prose], [class~=not-prose] *)) { + font-weight: 800; + color: inherit; +} +.prose :where(h3):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + font-size: 1.25em; + margin-top: 1.6em; + margin-bottom: 0.6em; + line-height: 1.6; +} +.prose :where(h3 strong):not(:where([class~=not-prose], [class~=not-prose] *)) { + font-weight: 700; + color: inherit; +} +.prose :where(h4):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; + line-height: 1.5; +} +.prose :where(h4 strong):not(:where([class~=not-prose], [class~=not-prose] *)) { + font-weight: 700; + color: inherit; +} +.prose :where(img):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 2em; + margin-bottom: 2em; +} +.prose :where(picture):not(:where([class~=not-prose], [class~=not-prose] *)) { + display: block; + margin-top: 2em; + margin-bottom: 2em; +} +.prose :where(kbd):not(:where([class~=not-prose], [class~=not-prose] *)) { + font-weight: 500; + font-family: inherit; + color: var(--tw-prose-kbd); + box-shadow: 0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%), 0 3px 0 rgb(var(--tw-prose-kbd-shadows)/10%); + font-size: 0.875em; + border-radius: 0.3125rem; + padding-top: 0.1875em; + padding-right: 0.375em; + padding-bottom: 0.1875em; + padding-left: 0.375em; +} +.prose :where(code):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-code); + font-weight: 600; + font-size: 0.875em; +} +.prose :where(code):not(:where([class~=not-prose], [class~=not-prose] *))::before { + content: "`"; +} +.prose :where(code):not(:where([class~=not-prose], [class~=not-prose] *))::after { + content: "`"; +} +.prose :where(a code):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; +} +.prose :where(h1 code):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; +} +.prose :where(h2 code):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; + font-size: 0.875em; +} +.prose :where(h3 code):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; + font-size: 0.9em; +} +.prose :where(h4 code):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; +} +.prose :where(blockquote code):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; +} +.prose :where(thead th code):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: inherit; +} +.prose :where(pre):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-pre-code); + background-color: var(--tw-prose-pre-bg); + overflow-x: auto; + font-weight: 400; + font-size: 0.875em; + line-height: 1.7142857; + margin-top: 1.7142857em; + margin-bottom: 1.7142857em; + border-radius: 0.375rem; + padding-top: 0.8571429em; + padding-right: 1.1428571em; + padding-bottom: 0.8571429em; + padding-left: 1.1428571em; +} +.prose :where(pre code):not(:where([class~=not-prose], [class~=not-prose] *)) { + background-color: transparent; + border-width: 0; + border-radius: 0; + padding: 0; + font-weight: inherit; + color: inherit; + font-size: inherit; + font-family: inherit; + line-height: inherit; +} +.prose :where(pre code):not(:where([class~=not-prose], [class~=not-prose] *))::before { + content: none; +} +.prose :where(pre code):not(:where([class~=not-prose], [class~=not-prose] *))::after { + content: none; +} +.prose :where(table):not(:where([class~=not-prose], [class~=not-prose] *)) { + width: 100%; + table-layout: auto; + text-align: left; + margin-top: 2em; + margin-bottom: 2em; + font-size: 0.875em; + line-height: 1.7142857; +} +.prose :where(thead):not(:where([class~=not-prose], [class~=not-prose] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-th-borders); +} +.prose :where(thead th):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + vertical-align: bottom; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} +.prose :where(tbody tr):not(:where([class~=not-prose], [class~=not-prose] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-td-borders); +} +.prose :where(tbody tr:last-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + border-bottom-width: 0; +} +.prose :where(tbody td):not(:where([class~=not-prose], [class~=not-prose] *)) { + vertical-align: baseline; +} +.prose :where(tfoot):not(:where([class~=not-prose], [class~=not-prose] *)) { + border-top-width: 1px; + border-top-color: var(--tw-prose-th-borders); +} +.prose :where(tfoot td):not(:where([class~=not-prose], [class~=not-prose] *)) { + vertical-align: top; +} +.prose :where(figure > *):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0; + margin-bottom: 0; +} +.prose :where(figcaption):not(:where([class~=not-prose], [class~=not-prose] *)) { + color: var(--tw-prose-captions); + font-size: 0.875em; + line-height: 1.4285714; + margin-top: 0.8571429em; +} +.prose { + --tw-prose-body: #374151; + --tw-prose-headings: #111827; + --tw-prose-lead: #4b5563; + --tw-prose-links: #111827; + --tw-prose-bold: #111827; + --tw-prose-counters: #6b7280; + --tw-prose-bullets: #d1d5db; + --tw-prose-hr: #e5e7eb; + --tw-prose-quotes: #111827; + --tw-prose-quote-borders: #e5e7eb; + --tw-prose-captions: #6b7280; + --tw-prose-kbd: #111827; + --tw-prose-kbd-shadows: 17 24 39; + --tw-prose-code: #111827; + --tw-prose-pre-code: #e5e7eb; + --tw-prose-pre-bg: #1f2937; + --tw-prose-th-borders: #d1d5db; + --tw-prose-td-borders: #e5e7eb; + --tw-prose-invert-body: #d1d5db; + --tw-prose-invert-headings: #fff; + --tw-prose-invert-lead: #9ca3af; + --tw-prose-invert-links: #fff; + --tw-prose-invert-bold: #fff; + --tw-prose-invert-counters: #9ca3af; + --tw-prose-invert-bullets: #4b5563; + --tw-prose-invert-hr: #374151; + --tw-prose-invert-quotes: #f3f4f6; + --tw-prose-invert-quote-borders: #374151; + --tw-prose-invert-captions: #9ca3af; + --tw-prose-invert-kbd: #fff; + --tw-prose-invert-kbd-shadows: 255 255 255; + --tw-prose-invert-code: #fff; + --tw-prose-invert-pre-code: #d1d5db; + --tw-prose-invert-pre-bg: rgba(0, 0, 0, 0.5); + --tw-prose-invert-th-borders: #4b5563; + --tw-prose-invert-td-borders: #374151; + font-size: 1rem; + line-height: 1.75; +} +.prose :where(picture > img):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0; + margin-bottom: 0; +} +.prose :where(video):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 2em; + margin-bottom: 2em; +} +.prose :where(li):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0.5em; + margin-bottom: 0.5em; +} +.prose :where(ol > li):not(:where([class~=not-prose], [class~=not-prose] *)) { + padding-left: 0.375em; +} +.prose :where(ul > li):not(:where([class~=not-prose], [class~=not-prose] *)) { + padding-left: 0.375em; +} +.prose :where(.prose > ul > li p):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} +.prose :where(.prose > ul > li > *:first-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 1.25em; +} +.prose :where(.prose > ul > li > *:last-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-bottom: 1.25em; +} +.prose :where(.prose > ol > li > *:first-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 1.25em; +} +.prose :where(.prose > ol > li > *:last-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-bottom: 1.25em; +} +.prose :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} +.prose :where(dl):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} +.prose :where(dd):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0.5em; + padding-left: 1.625em; +} +.prose :where(hr + *):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0; +} +.prose :where(h2 + *):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0; +} +.prose :where(h3 + *):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0; +} +.prose :where(h4 + *):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0; +} +.prose :where(thead th:first-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + padding-left: 0; +} +.prose :where(thead th:last-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + padding-right: 0; +} +.prose :where(tbody td, tfoot td):not(:where([class~=not-prose], [class~=not-prose] *)) { + padding-top: 0.5714286em; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} +.prose :where(tbody td:first-child, tfoot td:first-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + padding-left: 0; +} +.prose :where(tbody td:last-child, tfoot td:last-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + padding-right: 0; +} +.prose :where(figure):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 2em; + margin-bottom: 2em; +} +.prose :where(.prose > :first-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-top: 0; +} +.prose :where(.prose > :last-child):not(:where([class~=not-prose], [class~=not-prose] *)) { + margin-bottom: 0; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} +.static { + position: static; +} +.absolute { + position: absolute; +} +.relative { + position: relative; +} +.sticky { + position: sticky; +} +.bottom-0 { + bottom: 0px; +} +.left-0 { + left: 0px; +} +.top-0 { + top: 0px; +} +.top-8 { + top: 2rem; +} +.top-full { + top: 100%; +} +.isolate { + isolation: isolate; +} +.z-10 { + z-index: 10; +} +.z-20 { + z-index: 20; +} +.z-30 { + z-index: 30; +} +.z-50 { + z-index: 50; +} +.col-span-2 { + grid-column: span 2/span 2; +} +.col-span-4 { + grid-column: span 4/span 4; +} +.col-span-5 { + grid-column: span 5/span 5; +} +.m-auto { + margin: auto; +} +.mx-auto { + margin-left: auto; + margin-right: auto; +} +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} +.mb-12 { + margin-bottom: 3rem; +} +.mb-2 { + margin-bottom: 0.5rem; +} +.mb-28 { + margin-bottom: 7rem; +} +.mb-3 { + margin-bottom: 0.75rem; +} +.mb-4 { + margin-bottom: 1rem; +} +.mb-5 { + margin-bottom: 1.25rem; +} +.mb-8 { + margin-bottom: 2rem; +} +.ml-4 { + margin-left: 1rem; +} +.mr-4 { + margin-right: 1rem; +} +.mt-12 { + margin-top: 3rem; +} +.mt-2 { + margin-top: 0.5rem; +} +.mt-4 { + margin-top: 1rem; +} +.mt-6 { + margin-top: 1.5rem; +} +.mt-8 { + margin-top: 2rem; +} +.mt-auto { + margin-top: auto; +} +.inline-block { + display: inline-block; +} +.flex { + display: flex; +} +.inline-flex { + display: inline-flex; +} +.grid { + display: grid; +} +.hidden { + display: none; +} +.h-16 { + height: 4rem; +} +.h-4 { + height: 1rem; +} +.h-44 { + height: 11rem; +} +.h-5 { + height: 1.25rem; +} +.h-56 { + height: 14rem; +} +.h-full { + height: 100%; +} +.max-h-full { + max-height: 100%; +} +.w-11\/12 { + width: 91.666667%; +} +.w-16 { + width: 4rem; +} +.w-36 { + width: 9rem; +} +.w-4 { + width: 1rem; +} +.w-4\/6 { + width: 66.666667%; +} +.w-5 { + width: 1.25rem; +} +.w-7\/12 { + width: 58.333333%; +} +.w-full { + width: 100%; +} +.min-w-full { + min-width: 100%; +} +.min-w-max { + min-width: -moz-max-content; + min-width: max-content; +} +.max-w-2xl { + max-width: 42rem; +} +.max-w-6xl { + max-width: 72rem; +} +.max-w-7xl { + max-width: 80rem; +} +.max-w-max { + max-width: -moz-max-content; + max-width: max-content; +} +.max-w-none { + max-width: none; +} +.max-w-screen-xl { + max-width: 1280px; +} +.flex-1 { + flex: 1 1 0%; +} +.flex-shrink-0 { + flex-shrink: 0; +} +.-rotate-180 { + --tw-rotate: -180deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(-180deg) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.-skew-x-12 { + --tw-skew-x: -12deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(-12deg) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.skew-x-12 { + --tw-skew-x: 12deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(12deg) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.cursor-pointer { + cursor: pointer; +} +.list-disc { + list-style-type: disc; +} +.list-none { + list-style-type: none; +} +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} +.flex-col { + flex-direction: column; +} +.flex-col-reverse { + flex-direction: column-reverse; +} +.items-start { + align-items: flex-start; +} +.items-center { + align-items: center; +} +.items-baseline { + align-items: baseline; +} +.justify-end { + justify-content: flex-end; +} +.justify-center { + justify-content: center; +} +.justify-between { + justify-content: space-between; +} +.gap-4 { + gap: 1rem; +} +.gap-6 { + gap: 1.5rem; +} +.gap-8 { + gap: 2rem; +} +.gap-x-16 { + -moz-column-gap: 4rem; + column-gap: 4rem; +} +.gap-x-2 { + -moz-column-gap: 0.5rem; + column-gap: 0.5rem; +} +.gap-y-4 { + row-gap: 1rem; +} +.gap-y-8 { + row-gap: 2rem; +} +.-space-x-px > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(-1px * 0); + margin-right: calc(-1px * var(--tw-space-x-reverse)); + margin-left: calc(-1px * (1 - 0)); + margin-left: calc(-1px * (1 - var(--tw-space-x-reverse))); +} +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * 0); + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * (1 - 0)); + margin-left: calc(1rem * (1 - var(--tw-space-x-reverse))); +} +.space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * (1 - 0)); + margin-top: calc(0px * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * 0); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); +} +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * (1 - 0)); + margin-top: calc(0.25rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * 0); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * (1 - 0)); + margin-top: calc(0.5rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * 0); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * (1 - 0)); + margin-top: calc(1rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * 0); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} +.overflow-hidden { + overflow: hidden; +} +.overflow-x-hidden { + overflow-x: hidden; +} +.rounded { + border-radius: 0.25rem; +} +.rounded-full { + border-radius: 9999px; +} +.rounded-lg { + border-radius: 0.5rem; +} +.rounded-md { + border-radius: 0.375rem; +} +.border { + border-width: 1px; +} +.border-t { + border-top-width: 1px; +} +.border-black { + --tw-border-opacity: 1; + border-color: rgba(0, 0, 0,1); + border-color: rgba(0, 0, 0,var(--tw-border-opacity)); +} +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgba(229, 231, 235,1); + border-color: rgba(229, 231, 235,var(--tw-border-opacity)); +} +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgba(209, 213, 219,1); + border-color: rgba(209, 213, 219,var(--tw-border-opacity)); +} +.border-indigo-500 { + --tw-border-opacity: 1; + border-color: rgba(99, 102, 241,1); + border-color: rgba(99, 102, 241,var(--tw-border-opacity)); +} +.border-white { + --tw-border-opacity: 1; + border-color: rgba(255, 255, 255,1); + border-color: rgba(255, 255, 255,var(--tw-border-opacity)); +} +.border-yellow-500 { + --tw-border-opacity: 1; + border-color: rgba(250, 204, 21,1); + border-color: rgba(250, 204, 21,var(--tw-border-opacity)); +} +.bg-blue-50 { + --tw-bg-opacity: 1; + background-color: rgba(218, 223, 227,1); + background-color: rgba(218, 223, 227,var(--tw-bg-opacity)); +} +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgba(10, 39, 68,1); + background-color: rgba(10, 39, 68,var(--tw-bg-opacity)); +} +.bg-blue-600 { + --tw-bg-opacity: 1; + background-color: rgba(11, 33, 56,1); + background-color: rgba(11, 33, 56,var(--tw-bg-opacity)); +} +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgba(243, 244, 246,1); + background-color: rgba(243, 244, 246,var(--tw-bg-opacity)); +} +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235,1); + background-color: rgba(229, 231, 235,var(--tw-bg-opacity)); +} +.bg-green-400 { + --tw-bg-opacity: 1; + background-color: rgba(52, 211, 153,1); + background-color: rgba(52, 211, 153,var(--tw-bg-opacity)); +} +.bg-indigo-50 { + --tw-bg-opacity: 1; + background-color: rgba(238, 242, 255,1); + background-color: rgba(238, 242, 255,var(--tw-bg-opacity)); +} +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgba(192, 0, 0,1); + background-color: rgba(192, 0, 0,var(--tw-bg-opacity)); +} +.bg-white { + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255,1); + background-color: rgba(255, 255, 255,var(--tw-bg-opacity)); +} +.bg-opacity-50 { + --tw-bg-opacity: 0.5; +} +.bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); +} +.from-white { + --tw-gradient-from: #fff var(--tw-gradient-from-position); + --tw-gradient-to: rgba(255, 255, 255, 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} +.via-transparent { + --tw-gradient-to: rgba(0, 0, 0, 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), transparent var(--tw-gradient-via-position), var(--tw-gradient-to); +} +.to-white { + --tw-gradient-to: #fff var(--tw-gradient-to-position); +} +.bg-cover { + background-size: cover; +} +.bg-top { + background-position: top; +} +.object-contain { + -o-object-fit: contain; + object-fit: contain; +} +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} +.object-center { + -o-object-position: center; + object-position: center; +} +.object-left-top { + -o-object-position: left top; + object-position: left top; +} +.object-top { + -o-object-position: top; + object-position: top; +} +.p-2 { + padding: 0.5rem; +} +.p-3 { + padding: 0.75rem; +} +.p-4 { + padding: 1rem; +} +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} +.py-16 { + padding-top: 4rem; + padding-bottom: 4rem; +} +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} +.pb-12 { + padding-bottom: 3rem; +} +.pb-16 { + padding-bottom: 4rem; +} +.pb-28 { + padding-bottom: 7rem; +} +.pb-8 { + padding-bottom: 2rem; +} +.pl-8 { + padding-left: 2rem; +} +.pt-1 { + padding-top: 0.25rem; +} +.pt-10 { + padding-top: 2.5rem; +} +.pt-12 { + padding-top: 3rem; +} +.pt-24 { + padding-top: 6rem; +} +.pt-28 { + padding-top: 7rem; +} +.pt-36 { + padding-top: 9rem; +} +.pt-8 { + padding-top: 2rem; +} +.text-left { + text-align: left; +} +.text-center { + text-align: center; +} +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} +.font-black { + font-weight: 900; +} +.font-bold { + font-weight: 700; +} +.font-medium { + font-weight: 500; +} +.font-normal { + font-weight: 400; +} +.font-semibold { + font-weight: 600; +} +.uppercase { + text-transform: uppercase; +} +.capitalize { + text-transform: capitalize; +} +.leading-6 { + line-height: 1.5rem; +} +.tracking-tight { + letter-spacing: -0.025em; +} +.text-black { + --tw-text-opacity: 1; + color: rgba(0, 0, 0,1); + color: rgba(0, 0, 0,var(--tw-text-opacity)); +} +.text-blue-500 { + --tw-text-opacity: 1; + color: rgba(10, 39, 68,1); + color: rgba(10, 39, 68,var(--tw-text-opacity)); +} +.text-gray-400 { + --tw-text-opacity: 1; + color: rgba(156, 163, 175,1); + color: rgba(156, 163, 175,var(--tw-text-opacity)); +} +.text-gray-500 { + --tw-text-opacity: 1; + color: rgba(107, 114, 128,1); + color: rgba(107, 114, 128,var(--tw-text-opacity)); +} +.text-gray-600 { + --tw-text-opacity: 1; + color: rgba(75, 85, 99,1); + color: rgba(75, 85, 99,var(--tw-text-opacity)); +} +.text-gray-700 { + --tw-text-opacity: 1; + color: rgba(55, 65, 81,1); + color: rgba(55, 65, 81,var(--tw-text-opacity)); +} +.text-gray-900 { + --tw-text-opacity: 1; + color: rgba(17, 24, 39,1); + color: rgba(17, 24, 39,var(--tw-text-opacity)); +} +.text-indigo-600 { + --tw-text-opacity: 1; + color: rgba(79, 70, 229,1); + color: rgba(79, 70, 229,var(--tw-text-opacity)); +} +.text-inherit { + color: inherit; +} +.text-purple-900 { + --tw-text-opacity: 1; + color: rgba(67, 20, 44,1); + color: rgba(67, 20, 44,var(--tw-text-opacity)); +} +.text-red-500 { + --tw-text-opacity: 1; + color: rgba(192, 0, 0,1); + color: rgba(192, 0, 0,var(--tw-text-opacity)); +} +.text-white { + --tw-text-opacity: 1; + color: rgba(255, 255, 255,1); + color: rgba(255, 255, 255,var(--tw-text-opacity)); +} +.underline { + text-decoration-line: underline; +} +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.shadow { + --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: 0 0 rgba(0, 0, 0, 0), 0 0 rgba(0, 0, 0, 0), 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1); + box-shadow: var(--tw-ring-offset-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-ring-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-shadow); +} +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: 0 0 rgba(0, 0, 0, 0), 0 0 rgba(0, 0, 0, 0), 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); + box-shadow: var(--tw-ring-offset-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-ring-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-shadow); +} +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: 0 0 rgba(0, 0, 0, 0), 0 0 rgba(0, 0, 0, 0), 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-ring-shadow, 0 0 rgba(0, 0, 0, 0)), var(--tw-shadow); +} +.outline { + outline-style: solid; +} +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.duration-300 { + transition-duration: 300ms; +} +.duration-700 { + transition-duration: 700ms; +} +.ease-in-out { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +/* Pinegrow generated Design Panel Extra Rules Begin */ +h1, h2, h3, h4 { + font-family: "Montserrat", sans-serif; +} +body, html { + min-height: 100vh; +} +/* Pinegrow generated Design Panel Extra Rules End */ +.hover\:-translate-y-2:hover { + --tw-translate-y: -0.5rem; + transform: translate(var(--tw-translate-x), -0.5rem) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.hover\:bg-gray-200:hover { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235,1); + background-color: rgba(229, 231, 235,var(--tw-bg-opacity)); +} +.hover\:bg-gray-50:hover { + --tw-bg-opacity: 1; + background-color: rgba(249, 250, 251,1); + background-color: rgba(249, 250, 251,var(--tw-bg-opacity)); +} +.hover\:text-black:hover { + --tw-text-opacity: 1; + color: rgba(0, 0, 0,1); + color: rgba(0, 0, 0,var(--tw-text-opacity)); +} +.hover\:text-blue-100:hover { + --tw-text-opacity: 1; + color: rgba(175, 182, 193,1); + color: rgba(175, 182, 193,var(--tw-text-opacity)); +} +.hover\:text-yellow-500:hover { + --tw-text-opacity: 1; + color: rgba(250, 204, 21,1); + color: rgba(250, 204, 21,var(--tw-text-opacity)); +} +.hover\:underline:hover { + text-decoration-line: underline; +} +.hover\:duration-700:hover { + transition-duration: 700ms; +} +.hover\:ease-in-out:hover { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.focus\:z-20:focus { + z-index: 20; +} +.group:hover .group-hover\:translate-y-0 { + --tw-translate-y: 0px; + transform: translate(var(--tw-translate-x), 0px) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +@media (prefers-color-scheme: dark) { + .dark\:bg-gray-900 { + --tw-bg-opacity: 1; + background-color: rgba(17, 24, 39,1); + background-color: rgba(17, 24, 39,var(--tw-bg-opacity)); + } +} +@media (min-width: 640px) { + .sm\:rounded-lg { + border-radius: 0.5rem; + } + .sm\:p-6 { + padding: 1.5rem; + } + .sm\:text-5xl { + font-size: 3rem; + line-height: 1; + } +} +@media (min-width: 768px) { + .md\:absolute { + position: absolute; + } + .md\:left-0 { + left: 0px; + } + .md\:my-4 { + margin-top: 1rem; + margin-bottom: 1rem; + } + .md\:mb-14 { + margin-bottom: 3.5rem; + } + .md\:mt-4 { + margin-top: 1rem; + } + .md\:block { + display: block; + } + .md\:flex { + display: flex; + } + .md\:grid { + display: grid; + } + .md\:h-96 { + height: 24rem; + } + .md\:h-full { + height: 100%; + } + .md\:h-\[1080px\] { + height: 1080px; + } + .md\:h-\[720px\] { + height: 720px; + } + .md\:h-\[360px\] { + height: 360px; + } + .md\:w-1\/2 { + width: 50%; + } + .md\:w-6\/12 { + width: 50%; + } + .md\:w-max { + width: -moz-max-content; + width: max-content; + } + .md\:max-w-7xl { + max-width: 80rem; + } + .md\:max-w-md { + max-width: 28rem; + } + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .md\:flex-row { + flex-direction: row; + } + .md\:flex-row-reverse { + flex-direction: row-reverse; + } + .md\:items-end { + align-items: flex-end; + } + .md\:gap-2 { + gap: 0.5rem; + } + .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * 0); + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * (1 - 0)); + margin-left: calc(1rem * (1 - var(--tw-space-x-reverse))); + } + .md\:p-12 { + padding: 3rem; + } + .md\:px-4 { + padding-left: 1rem; + padding-right: 1rem; + } + .md\:px-8 { + padding-left: 2rem; + padding-right: 2rem; + } + .md\:py-8 { + padding-top: 2rem; + padding-bottom: 2rem; + } + .md\:text-2xl { + font-size: 1.5rem; + line-height: 2rem; + } + .md\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } + .md\:text-lg { + font-size: 1.125rem; + line-height: 1.75rem; + } + .md\:text-xl { + font-size: 1.25rem; + line-height: 1.75rem; + } + .md\:font-semibold { + font-weight: 600; + } +} +@media (min-width: 1024px) { + .lg\:relative { + position: relative; + } + .lg\:-top-32 { + top: -8rem; + } + .lg\:left-4 { + left: 1rem; + } + .lg\:left-8 { + left: 2rem; + } + .lg\:col-span-2 { + grid-column: span 2/span 2; + } + .lg\:col-span-3 { + grid-column: span 3/span 3; + } + .lg\:mx-auto { + margin-left: auto; + margin-right: auto; + } + .lg\:mb-0 { + margin-bottom: 0px; + } + .lg\:mr-2 { + margin-right: 0.5rem; + } + .lg\:mt-0 { + margin-top: 0px; + } + .lg\:block { + display: block; + } + .lg\:flex { + display: flex; + } + .lg\:grid { + display: grid; + } + .lg\:hidden { + display: none; + } + .lg\:h-28 { + height: 7rem; + } + .lg\:h-32 { + height: 8rem; + } + .lg\:h-96 { + height: 24rem; + } + .lg\:h-full { + height: 100%; + } + .lg\:h-\[720px\] { + height: 720px; + } + .lg\:w-2\/4 { + width: 50%; + } + .lg\:w-32 { + width: 8rem; + } + .lg\:max-w-7xl { + max-width: 80rem; + } + .lg\:max-w-screen-2xl { + max-width: 1536px; + } + .lg\:translate-y-full { + --tw-translate-y: 100%; + transform: translate(var(--tw-translate-x), 100%) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .lg\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .lg\:grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .lg\:grid-cols-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + .lg\:flex-col { + flex-direction: column; + } + .lg\:items-center { + align-items: center; + } + .lg\:items-stretch { + align-items: stretch; + } + .lg\:justify-end { + justify-content: flex-end; + } + .lg\:justify-between { + justify-content: space-between; + } + .lg\:gap-8 { + gap: 2rem; + } + .lg\:gap-x-4 { + -moz-column-gap: 1rem; + column-gap: 1rem; + } + .lg\:gap-y-0 { + row-gap: 0px; + } + .lg\:space-x-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1.25rem * 0); + margin-right: calc(1.25rem * var(--tw-space-x-reverse)); + margin-left: calc(1.25rem * (1 - 0)); + margin-left: calc(1.25rem * (1 - var(--tw-space-x-reverse))); + } + .lg\:space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * (1 - 0)); + margin-top: calc(0px * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * 0); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); + } + .lg\:space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * (1 - 0)); + margin-top: calc(1.5rem * (1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * 0); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); + } + .lg\:rounded-md { + border-radius: 0.375rem; + } + .lg\:bg-white { + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255,1); + background-color: rgba(255, 255, 255,var(--tw-bg-opacity)); + } + .lg\:p-0 { + padding: 0px; + } + .lg\:p-28 { + padding: 7rem; + } + .lg\:px-0 { + padding-left: 0px; + padding-right: 0px; + } + .lg\:px-16 { + padding-left: 4rem; + padding-right: 4rem; + } + .lg\:px-4 { + padding-left: 1rem; + padding-right: 1rem; + } + .lg\:py-0 { + padding-top: 0px; + padding-bottom: 0px; + } + .lg\:py-16 { + padding-top: 4rem; + padding-bottom: 4rem; + } + .lg\:py-28 { + padding-top: 7rem; + padding-bottom: 7rem; + } + .lg\:py-4 { + padding-top: 1rem; + padding-bottom: 1rem; + } + .lg\:py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; + } + .lg\:py-8 { + padding-top: 2rem; + padding-bottom: 2rem; + } + .lg\:pl-4 { + padding-left: 1rem; + } + .lg\:pr-32 { + padding-right: 8rem; + } + .lg\:pr-8 { + padding-right: 2rem; + } + .lg\:pt-28 { + padding-top: 7rem; + } + .lg\:text-2xl { + font-size: 1.5rem; + line-height: 2rem; + } + .lg\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } + .lg\:text-5xl { + font-size: 3rem; + line-height: 1; + } + .lg\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } +} +@media (min-width: 1280px) { + .xl\:max-w-7xl { + max-width: 80rem; + } + .xl\:pr-16 { + padding-right: 4rem; + } + .xl\:text-center { + text-align: center; + } +} +@media (min-width: 1536px) { + .\32 xl\:mx-auto { + margin-left: auto; + margin-right: auto; + } + .\32 xl\:ml-auto { + margin-left: auto; + } + .\32 xl\:mr-auto { + margin-right: auto; + } + .\32 xl\:max-w-screen-2xl { + max-width: 1536px; + } + .\32 xl\:pl-44 { + padding-left: 11rem; + } + .\32 xl\:pr-44 { + padding-right: 11rem; + } + .\32 xl\:text-right { + text-align: right; + } + .\32 xl\:capitalize { + text-transform: capitalize; + } +} +.toggle-menu { + width: 30px; + height: 50px; + position: relative; + z-index: 1000; +} +.toggle-menu i { + position: absolute; + height: 2px; + background: #2f2057; + background: #040e18; + background: var(--primary-900); + width: 30px; + transition: all 0.3s; +} +.toggle-menu i:nth-child(1) { + top: 16px; +} +.toggle-menu i:nth-child(2) { + top: 24px; +} +.toggle-menu i:nth-child(3) { + top: 32px; +} +.toggle-menu.active i:nth-child(1) { + top: 25px; + transform: rotateZ(45deg); +} +.toggle-menu.active i:nth-child(2) { + background: transparent; + display: none; +} +.toggle-menu.active i:nth-child(3) { + top: 25px; + transform: rotateZ(-45deg); +} +#menu_wrapper { + height: 0px; + transition: height 600ms ease-in-out; +} +#menu_wrapper.expand { + height: 100vh; +} +@media screen and (min-width: 1024px) { + #menu_wrapper { + height: auto; + } + #menu_wrapper.expand { + height: auto; + } +} +#main_menu li a:after { + background: none repeat scroll 0 0 transparent; + bottom: 0; + content: ""; + display: block; + height: 2px; + left: 50%; + position: absolute; + background: #fff; + transition: width 0.3s ease 0s, left 0.3s ease 0s; + width: 0; +} +#main_menu li a:hover:after { + width: 100%; + left: 0; +} +.active-highlight { + color: #ffcc33; + -webkit-text-decoration: underline; + text-decoration: underline; + font-weight: bold; +} +body.noScroll { + height: 100%; + overflow: hidden; +} +#footer li a { + position: relative; +} +#footer li a:after { + background: none repeat scroll 0 0 transparent; + bottom: 0; + content: ""; + display: block; + height: 2px; + left: 50%; + position: absolute; + background: #0a2744; + background: var(--primary-500); + transition: width 0.3s ease 0s, left 0.3s ease 0s; + width: 0; +} +#footer li a:hover:after { + width: 100%; + left: 0; +} +/* HERO SECTION */ +#hero { + height: 100vh; + background-image: url(../../assets/images/bgs/team_of_pharmacists.jpeg); + background-size: cover; + background-repeat: no-repeat; + background-position: bottom; + background-color: var(--white-500); + background-blend-mode: multiply; +} +@media screen and (min-width: 1024px) { + #hero { + height: 90vh; + background-position: center center; + } +} +#leon-difference_1 { + background-image: url(../../assets/images/bgs/pharmacist_smiling.jpeg); +} +#leon-difference_2 { + background-image: url(../../assets/images/bgs/hero-bg.jpeg); + filter: brightness(1.1); +} +/* TEMPLATE HEADERS */ +.page-header { + background-image: url("../../assets/images/bgs/page-bg.jpeg"); + background-size: cover; + background-position: bottom 25% center; + background-color: #858fa0; + background-color: var(--primary-200); + background-blend-mode: multiply; +} +.services-header { + background-image: url("../../assets/images/bgs/services-bg.jpeg"); + background-size: cover; + background-position: top 40% center; + background-color: #858fa0; + background-color: var(--primary-200); + background-blend-mode: multiply; +} +.category-header { + background-image: url("../../assets/images/bgs/pharmacist_advising.jpeg"); + background-size: cover; + background-position: top 60% center; + background-color: #858fa0; + background-color: var(--primary-200); + background-blend-mode: multiply; +} +.post-header { + background-image: url("../../assets/images/bgs/pharmacist_getting_prescription.jpg"); + background-size: cover; + background-position: center center; + background-color: #858fa0; + background-color: var(--primary-200); + background-blend-mode: multiply; +} diff --git a/assets/css/tailwind_for_wp_editor.css b/assets/css/tailwind_for_wp_editor.css new file mode 100644 index 0000000..a03f1cf --- /dev/null +++ b/assets/css/tailwind_for_wp_editor.css @@ -0,0 +1,1516 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat:100,200,300,400,500,600,700,800,900|Inter:100,200,300,400,500,600,700,800,900&display=swap'); + +*, +::before, +::after { + box-sizing: border-box; +} + +a { + color: inherit; + text-decoration: inherit; +} +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +html { + font-family: 'Montserrat', sans-serif; +} + +.container { + width: 100%; +} +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} +.prose { + color: var(--tw-prose-body); + max-width: 65ch; +} +.prose :where(p):not(:where([class~='not-prose'] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} +.prose :where([class~='lead']):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-lead); + font-size: 1.25em; + line-height: 1.6; + margin-top: 1.2em; + margin-bottom: 1.2em; +} +.prose :where(a):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-links); + text-decoration: underline; + font-weight: 500; +} +.prose :where(strong):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-bold); + font-weight: 600; +} +.prose :where(a strong):not(:where([class~='not-prose'] *)) { + color: inherit; +} +.prose :where(blockquote strong):not(:where([class~='not-prose'] *)) { + color: inherit; +} +.prose :where(thead th strong):not(:where([class~='not-prose'] *)) { + color: inherit; +} +.prose :where(ol):not(:where([class~='not-prose'] *)) { + list-style-type: decimal; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-left: 1.625em; +} +.prose :where(ol[type='A']):not(:where([class~='not-prose'] *)) { + list-style-type: upper-alpha; +} +.prose :where(ol[type='a']):not(:where([class~='not-prose'] *)) { + list-style-type: lower-alpha; +} +.prose :where(ol[type='A' s]):not(:where([class~='not-prose'] *)) { + list-style-type: upper-alpha; +} +.prose :where(ol[type='a' s]):not(:where([class~='not-prose'] *)) { + list-style-type: lower-alpha; +} +.prose :where(ol[type='I']):not(:where([class~='not-prose'] *)) { + list-style-type: upper-roman; +} +.prose :where(ol[type='i']):not(:where([class~='not-prose'] *)) { + list-style-type: lower-roman; +} +.prose :where(ol[type='I' s]):not(:where([class~='not-prose'] *)) { + list-style-type: upper-roman; +} +.prose :where(ol[type='i' s]):not(:where([class~='not-prose'] *)) { + list-style-type: lower-roman; +} +.prose :where(ol[type='1']):not(:where([class~='not-prose'] *)) { + list-style-type: decimal; +} +.prose :where(ul):not(:where([class~='not-prose'] *)) { + list-style-type: disc; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-left: 1.625em; +} +.prose :where(ol > li):not(:where([class~='not-prose'] *))::marker { + font-weight: 400; + color: var(--tw-prose-counters); +} +.prose :where(ul > li):not(:where([class~='not-prose'] *))::marker { + color: var(--tw-prose-bullets); +} +.prose :where(hr):not(:where([class~='not-prose'] *)) { + border-color: var(--tw-prose-hr); + border-top-width: 1px; + margin-top: 3em; + margin-bottom: 3em; +} +.prose :where(blockquote):not(:where([class~='not-prose'] *)) { + font-weight: 500; + font-style: italic; + color: var(--tw-prose-quotes); + border-left-width: 0.25rem; + border-left-color: var(--tw-prose-quote-borders); + quotes: '\201C''\201D''\2018''\2019'; + margin-top: 1.6em; + margin-bottom: 1.6em; + padding-left: 1em; +} +.prose :where(blockquote p:first-of-type):not(:where([class~='not-prose'] *))::before { + content: open-quote; +} +.prose :where(blockquote p:last-of-type):not(:where([class~='not-prose'] *))::after { + content: close-quote; +} +.prose :where(h1):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-headings); + font-weight: 800; + font-size: 2.25em; + margin-top: 0; + margin-bottom: 0.8888889em; + line-height: 1.1111111; +} +.prose :where(h1 strong):not(:where([class~='not-prose'] *)) { + font-weight: 900; + color: inherit; +} +.prose :where(h2):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-headings); + font-weight: 700; + font-size: 1.5em; + margin-top: 2em; + margin-bottom: 1em; + line-height: 1.3333333; +} +.prose :where(h2 strong):not(:where([class~='not-prose'] *)) { + font-weight: 800; + color: inherit; +} +.prose :where(h3):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + font-size: 1.25em; + margin-top: 1.6em; + margin-bottom: 0.6em; + line-height: 1.6; +} +.prose :where(h3 strong):not(:where([class~='not-prose'] *)) { + font-weight: 700; + color: inherit; +} +.prose :where(h4):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; + line-height: 1.5; +} +.prose :where(h4 strong):not(:where([class~='not-prose'] *)) { + font-weight: 700; + color: inherit; +} +.prose :where(img):not(:where([class~='not-prose'] *)) { + margin-top: 2em; + margin-bottom: 2em; +} +.prose :where(figure > *):not(:where([class~='not-prose'] *)) { + margin-top: 0; + margin-bottom: 0; +} +.prose :where(figcaption):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-captions); + font-size: 0.875em; + line-height: 1.4285714; + margin-top: 0.8571429em; +} +.prose :where(code):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-code); + font-weight: 600; + font-size: 0.875em; +} +.prose :where(code):not(:where([class~='not-prose'] *))::before { + content: '`'; +} +.prose :where(code):not(:where([class~='not-prose'] *))::after { + content: '`'; +} +.prose :where(a code):not(:where([class~='not-prose'] *)) { + color: inherit; +} +.prose :where(h1 code):not(:where([class~='not-prose'] *)) { + color: inherit; +} +.prose :where(h2 code):not(:where([class~='not-prose'] *)) { + color: inherit; + font-size: 0.875em; +} +.prose :where(h3 code):not(:where([class~='not-prose'] *)) { + color: inherit; + font-size: 0.9em; +} +.prose :where(h4 code):not(:where([class~='not-prose'] *)) { + color: inherit; +} +.prose :where(blockquote code):not(:where([class~='not-prose'] *)) { + color: inherit; +} +.prose :where(thead th code):not(:where([class~='not-prose'] *)) { + color: inherit; +} +.prose :where(pre):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-pre-code); + background-color: var(--tw-prose-pre-bg); + overflow-x: auto; + font-weight: 400; + font-size: 0.875em; + line-height: 1.7142857; + margin-top: 1.7142857em; + margin-bottom: 1.7142857em; + border-radius: 0.375rem; + padding-top: 0.8571429em; + padding-right: 1.1428571em; + padding-bottom: 0.8571429em; + padding-left: 1.1428571em; +} +.prose :where(pre code):not(:where([class~='not-prose'] *)) { + background-color: transparent; + border-width: 0; + border-radius: 0; + padding: 0; + font-weight: inherit; + color: inherit; + font-size: inherit; + font-family: inherit; + line-height: inherit; +} +.prose :where(pre code):not(:where([class~='not-prose'] *))::before { + content: none; +} +.prose :where(pre code):not(:where([class~='not-prose'] *))::after { + content: none; +} +.prose :where(table):not(:where([class~='not-prose'] *)) { + width: 100%; + table-layout: auto; + text-align: left; + margin-top: 2em; + margin-bottom: 2em; + font-size: 0.875em; + line-height: 1.7142857; +} +.prose :where(thead):not(:where([class~='not-prose'] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-th-borders); +} +.prose :where(thead th):not(:where([class~='not-prose'] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + vertical-align: bottom; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} +.prose :where(tbody tr):not(:where([class~='not-prose'] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-td-borders); +} +.prose :where(tbody tr:last-child):not(:where([class~='not-prose'] *)) { + border-bottom-width: 0; +} +.prose :where(tbody td):not(:where([class~='not-prose'] *)) { + vertical-align: baseline; +} +.prose :where(tfoot):not(:where([class~='not-prose'] *)) { + border-top-width: 1px; + border-top-color: var(--tw-prose-th-borders); +} +.prose :where(tfoot td):not(:where([class~='not-prose'] *)) { + vertical-align: top; +} +.prose { + --tw-prose-body: #374151; + --tw-prose-headings: #111827; + --tw-prose-lead: #4b5563; + --tw-prose-links: #111827; + --tw-prose-bold: #111827; + --tw-prose-counters: #6b7280; + --tw-prose-bullets: #d1d5db; + --tw-prose-hr: #e5e7eb; + --tw-prose-quotes: #111827; + --tw-prose-quote-borders: #e5e7eb; + --tw-prose-captions: #6b7280; + --tw-prose-code: #111827; + --tw-prose-pre-code: #e5e7eb; + --tw-prose-pre-bg: #1f2937; + --tw-prose-th-borders: #d1d5db; + --tw-prose-td-borders: #e5e7eb; + --tw-prose-invert-body: #d1d5db; + --tw-prose-invert-headings: #fff; + --tw-prose-invert-lead: #9ca3af; + --tw-prose-invert-links: #fff; + --tw-prose-invert-bold: #fff; + --tw-prose-invert-counters: #9ca3af; + --tw-prose-invert-bullets: #4b5563; + --tw-prose-invert-hr: #374151; + --tw-prose-invert-quotes: #f3f4f6; + --tw-prose-invert-quote-borders: #374151; + --tw-prose-invert-captions: #9ca3af; + --tw-prose-invert-code: #fff; + --tw-prose-invert-pre-code: #d1d5db; + --tw-prose-invert-pre-bg: rgb(0 0 0 / 50%); + --tw-prose-invert-th-borders: #4b5563; + --tw-prose-invert-td-borders: #374151; + font-size: 1rem; + line-height: 1.75; +} +.prose :where(video):not(:where([class~='not-prose'] *)) { + margin-top: 2em; + margin-bottom: 2em; +} +.prose :where(figure):not(:where([class~='not-prose'] *)) { + margin-top: 2em; + margin-bottom: 2em; +} +.prose :where(li):not(:where([class~='not-prose'] *)) { + margin-top: 0.5em; + margin-bottom: 0.5em; +} +.prose :where(ol > li):not(:where([class~='not-prose'] *)) { + padding-left: 0.375em; +} +.prose :where(ul > li):not(:where([class~='not-prose'] *)) { + padding-left: 0.375em; +} +.prose :where(.prose > ul > li p):not(:where([class~='not-prose'] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} +.prose :where(.prose > ul > li > *:first-child):not(:where([class~='not-prose'] *)) { + margin-top: 1.25em; +} +.prose :where(.prose > ul > li > *:last-child):not(:where([class~='not-prose'] *)) { + margin-bottom: 1.25em; +} +.prose :where(.prose > ol > li > *:first-child):not(:where([class~='not-prose'] *)) { + margin-top: 1.25em; +} +.prose :where(.prose > ol > li > *:last-child):not(:where([class~='not-prose'] *)) { + margin-bottom: 1.25em; +} +.prose :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~='not-prose'] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} +.prose :where(hr + *):not(:where([class~='not-prose'] *)) { + margin-top: 0; +} +.prose :where(h2 + *):not(:where([class~='not-prose'] *)) { + margin-top: 0; +} +.prose :where(h3 + *):not(:where([class~='not-prose'] *)) { + margin-top: 0; +} +.prose :where(h4 + *):not(:where([class~='not-prose'] *)) { + margin-top: 0; +} +.prose :where(thead th:first-child):not(:where([class~='not-prose'] *)) { + padding-left: 0; +} +.prose :where(thead th:last-child):not(:where([class~='not-prose'] *)) { + padding-right: 0; +} +.prose :where(tbody td, tfoot td):not(:where([class~='not-prose'] *)) { + padding-top: 0.5714286em; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} +.prose :where(tbody td:first-child, tfoot td:first-child):not(:where([class~='not-prose'] *)) { + padding-left: 0; +} +.prose :where(tbody td:last-child, tfoot td:last-child):not(:where([class~='not-prose'] *)) { + padding-right: 0; +} +.prose :where(.prose > :first-child):not(:where([class~='not-prose'] *)) { + margin-top: 0; +} +.prose :where(.prose > :last-child):not(:where([class~='not-prose'] *)) { + margin-bottom: 0; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} +.static { + position: static; +} +.absolute { + position: absolute; +} +.relative { + position: relative; +} +.sticky { + position: sticky; +} +.bottom-0 { + bottom: 0px; +} +.left-0 { + left: 0px; +} +.top-0 { + top: 0px; +} +.top-8 { + top: 2rem; +} +.top-full { + top: 100%; +} +.isolate { + isolation: isolate; +} +.z-10 { + z-index: 10; +} +.z-20 { + z-index: 20; +} +.z-30 { + z-index: 30; +} +.z-50 { + z-index: 50; +} +.col-span-2 { + grid-column: span 2 / span 2; +} +.col-span-4 { + grid-column: span 4 / span 4; +} +.col-span-5 { + grid-column: span 5 / span 5; +} +.m-auto { + margin: auto; +} +.mx-auto { + margin-left: auto; + margin-right: auto; +} +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} +.mb-12 { + margin-bottom: 3rem; +} +.mb-2 { + margin-bottom: 0.5rem; +} +.mb-28 { + margin-bottom: 7rem; +} +.mb-3 { + margin-bottom: 0.75rem; +} +.mb-4 { + margin-bottom: 1rem; +} +.mb-5 { + margin-bottom: 1.25rem; +} +.mb-8 { + margin-bottom: 2rem; +} +.ml-4 { + margin-left: 1rem; +} +.mr-4 { + margin-right: 1rem; +} +.mt-12 { + margin-top: 3rem; +} +.mt-2 { + margin-top: 0.5rem; +} +.mt-4 { + margin-top: 1rem; +} +.mt-6 { + margin-top: 1.5rem; +} +.mt-8 { + margin-top: 2rem; +} +.mt-auto { + margin-top: auto; +} +.inline-block { + display: inline-block; +} +.flex { + display: flex; +} +.inline-flex { + display: inline-flex; +} +.grid { + display: grid; +} +.hidden { + display: none; +} +.h-16 { + height: 4rem; +} +.h-4 { + height: 1rem; +} +.h-44 { + height: 11rem; +} +.h-5 { + height: 1.25rem; +} +.h-56 { + height: 14rem; +} +.h-full { + height: 100%; +} +.max-h-full { + max-height: 100%; +} +.w-11\/12 { + width: 91.666667%; +} +.w-16 { + width: 4rem; +} +.w-36 { + width: 9rem; +} +.w-4 { + width: 1rem; +} +.w-4\/6 { + width: 66.666667%; +} +.w-5 { + width: 1.25rem; +} +.w-7\/12 { + width: 58.333333%; +} +.w-full { + width: 100%; +} +.min-w-full { + min-width: 100%; +} +.min-w-max { + min-width: -moz-max-content; + min-width: max-content; +} +.max-w-2xl { + max-width: 42rem; +} +.max-w-6xl { + max-width: 72rem; +} +.max-w-7xl { + max-width: 80rem; +} +.max-w-max { + max-width: -moz-max-content; + max-width: max-content; +} +.max-w-none { + max-width: none; +} +.max-w-screen-xl { + max-width: 1280px; +} +.flex-1 { + flex: 1 1 0%; +} +.flex-shrink-0 { + flex-shrink: 0; +} +.-rotate-180 { + --tw-rotate: -180deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.-skew-x-12 { + --tw-skew-x: -12deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.skew-x-12 { + --tw-skew-x: 12deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.cursor-pointer { + cursor: pointer; +} +.list-none { + list-style-type: none; +} +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} +.flex-col { + flex-direction: column; +} +.items-start { + align-items: flex-start; +} +.items-center { + align-items: center; +} +.items-baseline { + align-items: baseline; +} +.justify-end { + justify-content: flex-end; +} +.justify-center { + justify-content: center; +} +.justify-between { + justify-content: space-between; +} +.gap-4 { + gap: 1rem; +} +.gap-6 { + gap: 1.5rem; +} +.gap-8 { + gap: 2rem; +} +.gap-x-16 { + -moz-column-gap: 4rem; + column-gap: 4rem; +} +.gap-x-2 { + -moz-column-gap: 0.5rem; + column-gap: 0.5rem; +} +.gap-y-4 { + row-gap: 1rem; +} +.gap-y-8 { + row-gap: 2rem; +} +.-space-x-px > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(-1px * var(--tw-space-x-reverse)); + margin-left: calc(-1px * calc(1 - var(--tw-space-x-reverse))); +} +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} +.space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); +} +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} +.overflow-hidden { + overflow: hidden; +} +.rounded { + border-radius: 0.25rem; +} +.rounded-full { + border-radius: 9999px; +} +.rounded-lg { + border-radius: 0.5rem; +} +.rounded-md { + border-radius: 0.375rem; +} +.border { + border-width: 1px; +} +.border-t { + border-top-width: 1px; +} +.border-black { + --tw-border-opacity: 1; + border-color: rgb(0 0 0 / var(--tw-border-opacity)); +} +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); +} +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} +.border-white { + --tw-border-opacity: 1; + border-color: rgb(255 255 255 / var(--tw-border-opacity)); +} +.border-yellow-500 { + --tw-border-opacity: 1; + border-color: rgb(250 204 21 / var(--tw-border-opacity)); +} +.bg-blue-50 { + --tw-bg-opacity: 1; + background-color: rgb(218 223 227 / var(--tw-bg-opacity)); +} +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(10 39 68 / var(--tw-bg-opacity)); +} +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} +.bg-green-400 { + --tw-bg-opacity: 1; + background-color: rgb(52 211 153 / var(--tw-bg-opacity)); +} +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(192 0 0 / var(--tw-bg-opacity)); +} +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} +.bg-opacity-50 { + --tw-bg-opacity: 0.5; +} +.bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); +} +.from-white { + --tw-gradient-from: #fff var(--tw-gradient-from-position); + --tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} +.via-transparent { + --tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), transparent var(--tw-gradient-via-position), var(--tw-gradient-to); +} +.to-white { + --tw-gradient-to: #fff var(--tw-gradient-to-position); +} +.bg-cover { + background-size: cover; +} +.bg-top { + background-position: top; +} +.object-contain { + -o-object-fit: contain; + object-fit: contain; +} +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} +.object-center { + -o-object-position: center; + object-position: center; +} +.object-left-top { + -o-object-position: left top; + object-position: left top; +} +.object-top { + -o-object-position: top; + object-position: top; +} +.p-2 { + padding: 0.5rem; +} +.p-3 { + padding: 0.75rem; +} +.p-4 { + padding: 1rem; +} +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} +.px-8 { + padding-left: 2rem; + padding-right: 2rem; +} +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} +.py-16 { + padding-top: 4rem; + padding-bottom: 4rem; +} +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} +.pb-12 { + padding-bottom: 3rem; +} +.pb-16 { + padding-bottom: 4rem; +} +.pb-28 { + padding-bottom: 7rem; +} +.pb-8 { + padding-bottom: 2rem; +} +.pl-8 { + padding-left: 2rem; +} +.pt-1 { + padding-top: 0.25rem; +} +.pt-10 { + padding-top: 2.5rem; +} +.pt-12 { + padding-top: 3rem; +} +.pt-24 { + padding-top: 6rem; +} +.pt-28 { + padding-top: 7rem; +} +.pt-36 { + padding-top: 9rem; +} +.pt-8 { + padding-top: 2rem; +} +.text-left { + text-align: left; +} +.text-center { + text-align: center; +} +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} +.font-black { + font-weight: 900; +} +.font-bold { + font-weight: 700; +} +.font-medium { + font-weight: 500; +} +.font-normal { + font-weight: 400; +} +.font-semibold { + font-weight: 600; +} +.uppercase { + text-transform: uppercase; +} +.capitalize { + text-transform: capitalize; +} +.leading-6 { + line-height: 1.5rem; +} +.tracking-tight { + letter-spacing: -0.025em; +} +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} +.text-blue-500 { + --tw-text-opacity: 1; + color: rgb(10 39 68 / var(--tw-text-opacity)); +} +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} +.text-inherit { + color: inherit; +} +.text-purple-900 { + --tw-text-opacity: 1; + color: rgb(67 20 44 / var(--tw-text-opacity)); +} +.text-red-500 { + --tw-text-opacity: 1; + color: rgb(192 0 0 / var(--tw-text-opacity)); +} +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} +.underline { + text-decoration-line: underline; +} +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.duration-300 { + transition-duration: 300ms; +} +.duration-700 { + transition-duration: 700ms; +} +.ease-in-out { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +h1, +h2, +h3, +h4 { + font-family: 'Montserrat', sans-serif; +} +body, +html { + min-height: 100vh; +} +.hover\:-translate-y-2:hover { + --tw-translate-y: -0.5rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +.hover\:bg-gray-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} +.hover\:bg-gray-50:hover { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} +.hover\:text-black:hover { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} +.hover\:text-blue-100:hover { + --tw-text-opacity: 1; + color: rgb(175 182 193 / var(--tw-text-opacity)); +} +.hover\:text-yellow-500:hover { + --tw-text-opacity: 1; + color: rgb(250 204 21 / var(--tw-text-opacity)); +} +.hover\:underline:hover { + text-decoration-line: underline; +} +.hover\:duration-700:hover { + transition-duration: 700ms; +} +.hover\:ease-in-out:hover { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} +.focus\:z-20:focus { + z-index: 20; +} +.group:hover .group-hover\:translate-y-0 { + --tw-translate-y: 0px; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} +@media (prefers-color-scheme: dark) { + .dark\:bg-gray-900 { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); + } +} +@media (min-width: 640px) { + .sm\:rounded-lg { + border-radius: 0.5rem; + } + .sm\:p-6 { + padding: 1.5rem; + } + .sm\:text-5xl { + font-size: 3rem; + line-height: 1; + } +} +@media (min-width: 768px) { + .md\:absolute { + position: absolute; + } + .md\:left-0 { + left: 0px; + } + .md\:my-4 { + margin-top: 1rem; + margin-bottom: 1rem; + } + .md\:mb-14 { + margin-bottom: 3.5rem; + } + .md\:mt-4 { + margin-top: 1rem; + } + .md\:flex { + display: flex; + } + .md\:grid { + display: grid; + } + .md\:h-96 { + height: 24rem; + } + .md\:w-1\/2 { + width: 50%; + } + .md\:w-6\/12 { + width: 50%; + } + .md\:w-max { + width: -moz-max-content; + width: max-content; + } + .md\:max-w-7xl { + max-width: 80rem; + } + .md\:max-w-md { + max-width: 28rem; + } + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .md\:flex-row { + flex-direction: row; + } + .md\:flex-row-reverse { + flex-direction: row-reverse; + } + .md\:items-end { + align-items: flex-end; + } + .md\:gap-2 { + gap: 0.5rem; + } + .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); + } + .md\:p-12 { + padding: 3rem; + } + .md\:px-4 { + padding-left: 1rem; + padding-right: 1rem; + } + .md\:px-8 { + padding-left: 2rem; + padding-right: 2rem; + } + .md\:py-8 { + padding-top: 2rem; + padding-bottom: 2rem; + } + .md\:text-2xl { + font-size: 1.5rem; + line-height: 2rem; + } + .md\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } + .md\:text-lg { + font-size: 1.125rem; + line-height: 1.75rem; + } + .md\:text-xl { + font-size: 1.25rem; + line-height: 1.75rem; + } + .md\:font-semibold { + font-weight: 600; + } +} +@media (min-width: 1024px) { + .lg\:relative { + position: relative; + } + .lg\:-top-32 { + top: -8rem; + } + .lg\:left-16 { + left: 4rem; + } + .lg\:left-4 { + left: 1rem; + } + .lg\:col-span-2 { + grid-column: span 2 / span 2; + } + .lg\:col-span-3 { + grid-column: span 3 / span 3; + } + .lg\:mx-auto { + margin-left: auto; + margin-right: auto; + } + .lg\:mb-0 { + margin-bottom: 0px; + } + .lg\:mr-2 { + margin-right: 0.5rem; + } + .lg\:mt-0 { + margin-top: 0px; + } + .lg\:block { + display: block; + } + .lg\:flex { + display: flex; + } + .lg\:grid { + display: grid; + } + .lg\:hidden { + display: none; + } + .lg\:h-28 { + height: 7rem; + } + .lg\:h-32 { + height: 8rem; + } + .lg\:h-96 { + height: 24rem; + } + .lg\:h-full { + height: 100%; + } + .lg\:w-2\/4 { + width: 50%; + } + .lg\:w-32 { + width: 8rem; + } + .lg\:max-w-7xl { + max-width: 80rem; + } + .lg\:max-w-screen-2xl { + max-width: 1536px; + } + .lg\:translate-y-full { + --tw-translate-y: 100%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + } + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .lg\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .lg\:grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .lg\:flex-col { + flex-direction: column; + } + .lg\:items-center { + align-items: center; + } + .lg\:items-stretch { + align-items: stretch; + } + .lg\:justify-end { + justify-content: flex-end; + } + .lg\:justify-between { + justify-content: space-between; + } + .lg\:gap-8 { + gap: 2rem; + } + .lg\:gap-x-4 { + -moz-column-gap: 1rem; + column-gap: 1rem; + } + .lg\:gap-y-0 { + row-gap: 0px; + } + .lg\:space-x-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1.25rem * var(--tw-space-x-reverse)); + margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse))); + } + .lg\:space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); + } + .lg\:space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); + } + .lg\:rounded-md { + border-radius: 0.375rem; + } + .lg\:bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); + } + .lg\:p-0 { + padding: 0px; + } + .lg\:p-28 { + padding: 7rem; + } + .lg\:px-0 { + padding-left: 0px; + padding-right: 0px; + } + .lg\:px-16 { + padding-left: 4rem; + padding-right: 4rem; + } + .lg\:px-28 { + padding-left: 7rem; + padding-right: 7rem; + } + .lg\:px-4 { + padding-left: 1rem; + padding-right: 1rem; + } + .lg\:py-0 { + padding-top: 0px; + padding-bottom: 0px; + } + .lg\:py-16 { + padding-top: 4rem; + padding-bottom: 4rem; + } + .lg\:py-28 { + padding-top: 7rem; + padding-bottom: 7rem; + } + .lg\:py-4 { + padding-top: 1rem; + padding-bottom: 1rem; + } + .lg\:py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; + } + .lg\:py-8 { + padding-top: 2rem; + padding-bottom: 2rem; + } + .lg\:pl-4 { + padding-left: 1rem; + } + .lg\:pr-32 { + padding-right: 8rem; + } + .lg\:pt-28 { + padding-top: 7rem; + } + .lg\:text-2xl { + font-size: 1.5rem; + line-height: 2rem; + } + .lg\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } + .lg\:text-5xl { + font-size: 3rem; + line-height: 1; + } + .lg\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } +} +@media (min-width: 1280px) { + .xl\:max-w-7xl { + max-width: 80rem; + } + .xl\:pr-32 { + padding-right: 8rem; + } + .xl\:text-center { + text-align: center; + } +} +@media (min-width: 1536px) { + .\32xl\:mx-auto { + margin-left: auto; + margin-right: auto; + } + .\32xl\:ml-auto { + margin-left: auto; + } + .\32xl\:mr-auto { + margin-right: auto; + } + .\32xl\:max-w-screen-2xl { + max-width: 1536px; + } + .\32xl\:pl-44 { + padding-left: 11rem; + } + .\32xl\:pr-44 { + padding-right: 11rem; + } + .\32xl\:text-right { + text-align: right; + } + .\32xl\:capitalize { + text-transform: capitalize; + } +} diff --git a/assets/images/awards/2013.png b/assets/images/awards/2013.png new file mode 100644 index 0000000..b215eb5 Binary files /dev/null and b/assets/images/awards/2013.png differ diff --git a/assets/images/awards/2014.png b/assets/images/awards/2014.png new file mode 100644 index 0000000..d0266b7 Binary files /dev/null and b/assets/images/awards/2014.png differ diff --git a/assets/images/awards/2015.png b/assets/images/awards/2015.png new file mode 100644 index 0000000..1749041 Binary files /dev/null and b/assets/images/awards/2015.png differ diff --git a/assets/images/awards/2016.png b/assets/images/awards/2016.png new file mode 100644 index 0000000..bd6f152 Binary files /dev/null and b/assets/images/awards/2016.png differ diff --git a/assets/images/awards/2017.png b/assets/images/awards/2017.png new file mode 100644 index 0000000..029e9b5 Binary files /dev/null and b/assets/images/awards/2017.png differ diff --git a/assets/images/awards/2018.png b/assets/images/awards/2018.png new file mode 100644 index 0000000..c1da178 Binary files /dev/null and b/assets/images/awards/2018.png differ diff --git a/assets/images/awards/2019.png b/assets/images/awards/2019.png new file mode 100644 index 0000000..c1d4775 Binary files /dev/null and b/assets/images/awards/2019.png differ diff --git a/assets/images/awards/2020-reach.png b/assets/images/awards/2020-reach.png new file mode 100644 index 0000000..1d7e0e8 Binary files /dev/null and b/assets/images/awards/2020-reach.png differ diff --git a/assets/images/awards/2020.png b/assets/images/awards/2020.png new file mode 100644 index 0000000..071bb97 Binary files /dev/null and b/assets/images/awards/2020.png differ diff --git a/assets/images/awards/2021.png b/assets/images/awards/2021.png new file mode 100644 index 0000000..b8fc653 Binary files /dev/null and b/assets/images/awards/2021.png differ diff --git a/assets/images/awards/2022.png b/assets/images/awards/2022.png new file mode 100644 index 0000000..952735e Binary files /dev/null and b/assets/images/awards/2022.png differ diff --git a/assets/images/awards/2023-high-res.png b/assets/images/awards/2023-high-res.png new file mode 100644 index 0000000..285212b Binary files /dev/null and b/assets/images/awards/2023-high-res.png differ diff --git a/assets/images/awards/2023.png b/assets/images/awards/2023.png new file mode 100644 index 0000000..d36d1af Binary files /dev/null and b/assets/images/awards/2023.png differ diff --git a/assets/images/awards/award-wall.jpg b/assets/images/awards/award-wall.jpg new file mode 100644 index 0000000..6d5c238 Binary files /dev/null and b/assets/images/awards/award-wall.jpg differ diff --git a/assets/images/awards/business_excellence.png b/assets/images/awards/business_excellence.png new file mode 100644 index 0000000..7a23d25 Binary files /dev/null and b/assets/images/awards/business_excellence.png differ diff --git a/assets/images/bgs/close_up_pharmacist_computer.jpg b/assets/images/bgs/close_up_pharmacist_computer.jpg new file mode 100644 index 0000000..bf7bfe4 Binary files /dev/null and b/assets/images/bgs/close_up_pharmacist_computer.jpg differ diff --git a/assets/images/bgs/diabetes.jpeg b/assets/images/bgs/diabetes.jpeg new file mode 100644 index 0000000..d6f6d64 Binary files /dev/null and b/assets/images/bgs/diabetes.jpeg differ diff --git a/assets/images/bgs/gta.jpeg b/assets/images/bgs/gta.jpeg new file mode 100644 index 0000000..4b4354c Binary files /dev/null and b/assets/images/bgs/gta.jpeg differ diff --git a/assets/images/bgs/hero-bg.jpeg b/assets/images/bgs/hero-bg.jpeg new file mode 100644 index 0000000..b46ccd7 Binary files /dev/null and b/assets/images/bgs/hero-bg.jpeg differ diff --git a/assets/images/bgs/kerr_street_medical.jpg b/assets/images/bgs/kerr_street_medical.jpg new file mode 100644 index 0000000..fdd5baf Binary files /dev/null and b/assets/images/bgs/kerr_street_medical.jpg differ diff --git a/assets/images/bgs/leon-pharmacy-entrance.jpg b/assets/images/bgs/leon-pharmacy-entrance.jpg new file mode 100644 index 0000000..16ec81e Binary files /dev/null and b/assets/images/bgs/leon-pharmacy-entrance.jpg differ diff --git a/assets/images/bgs/leon-pharmacy.jpg b/assets/images/bgs/leon-pharmacy.jpg new file mode 100644 index 0000000..4346c70 Binary files /dev/null and b/assets/images/bgs/leon-pharmacy.jpg differ diff --git a/assets/images/bgs/medication_delivery.jpeg b/assets/images/bgs/medication_delivery.jpeg new file mode 100644 index 0000000..1ca28b4 Binary files /dev/null and b/assets/images/bgs/medication_delivery.jpeg differ diff --git a/assets/images/bgs/medication_on_shelves.jpg b/assets/images/bgs/medication_on_shelves.jpg new file mode 100644 index 0000000..6872b68 Binary files /dev/null and b/assets/images/bgs/medication_on_shelves.jpg differ diff --git a/assets/images/bgs/page-bg.jpeg b/assets/images/bgs/page-bg.jpeg new file mode 100644 index 0000000..ef43423 Binary files /dev/null and b/assets/images/bgs/page-bg.jpeg differ diff --git a/assets/images/bgs/patient-receiving-injection.jpeg b/assets/images/bgs/patient-receiving-injection.jpeg new file mode 100644 index 0000000..9ffea5d Binary files /dev/null and b/assets/images/bgs/patient-receiving-injection.jpeg differ diff --git a/assets/images/bgs/pharmacist-precribing-minor-ailment-medication.jpeg b/assets/images/bgs/pharmacist-precribing-minor-ailment-medication.jpeg new file mode 100644 index 0000000..0a03769 Binary files /dev/null and b/assets/images/bgs/pharmacist-precribing-minor-ailment-medication.jpeg differ diff --git a/assets/images/bgs/pharmacist_advising.jpeg b/assets/images/bgs/pharmacist_advising.jpeg new file mode 100644 index 0000000..8ae864c Binary files /dev/null and b/assets/images/bgs/pharmacist_advising.jpeg differ diff --git a/assets/images/bgs/pharmacist_getting_prescription.jpg b/assets/images/bgs/pharmacist_getting_prescription.jpg new file mode 100644 index 0000000..35ed35d Binary files /dev/null and b/assets/images/bgs/pharmacist_getting_prescription.jpg differ diff --git a/assets/images/bgs/pharmacist_smiling.jpeg b/assets/images/bgs/pharmacist_smiling.jpeg new file mode 100644 index 0000000..398c858 Binary files /dev/null and b/assets/images/bgs/pharmacist_smiling.jpeg differ diff --git a/assets/images/bgs/pharmacists_working.jpg b/assets/images/bgs/pharmacists_working.jpg new file mode 100644 index 0000000..9d1985c Binary files /dev/null and b/assets/images/bgs/pharmacists_working.jpg differ diff --git a/assets/images/bgs/pharmacy_wideview.jpg b/assets/images/bgs/pharmacy_wideview.jpg new file mode 100644 index 0000000..a3e08e6 Binary files /dev/null and b/assets/images/bgs/pharmacy_wideview.jpg differ diff --git a/assets/images/bgs/services-bg.jpeg b/assets/images/bgs/services-bg.jpeg new file mode 100644 index 0000000..ef66888 Binary files /dev/null and b/assets/images/bgs/services-bg.jpeg differ diff --git a/assets/images/bgs/shop_items.jpg b/assets/images/bgs/shop_items.jpg new file mode 100644 index 0000000..2aeed3c Binary files /dev/null and b/assets/images/bgs/shop_items.jpg differ diff --git a/assets/images/bgs/team_of_pharmacists.jpeg b/assets/images/bgs/team_of_pharmacists.jpeg new file mode 100644 index 0000000..e1df235 Binary files /dev/null and b/assets/images/bgs/team_of_pharmacists.jpeg differ diff --git a/assets/images/extras/Placeholder-Image---Landscape.svg b/assets/images/extras/Placeholder-Image---Landscape.svg new file mode 100644 index 0000000..5c96d76 --- /dev/null +++ b/assets/images/extras/Placeholder-Image---Landscape.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/extras/Placeholder-Video---Landscape.svg b/assets/images/extras/Placeholder-Video---Landscape.svg new file mode 100644 index 0000000..74c53da --- /dev/null +++ b/assets/images/extras/Placeholder-Video---Landscape.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/extras/allow-icon3.svg b/assets/images/extras/allow-icon3.svg new file mode 100644 index 0000000..2934134 --- /dev/null +++ b/assets/images/extras/allow-icon3.svg @@ -0,0 +1,2 @@ + + diff --git a/assets/images/extras/calendar-week.svg b/assets/images/extras/calendar-week.svg new file mode 100644 index 0000000..06d6995 --- /dev/null +++ b/assets/images/extras/calendar-week.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/assets/images/extras/company-logo.svg b/assets/images/extras/company-logo.svg new file mode 100644 index 0000000..2447445 --- /dev/null +++ b/assets/images/extras/company-logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/extras/deny-icon3.svg b/assets/images/extras/deny-icon3.svg new file mode 100644 index 0000000..9abc040 --- /dev/null +++ b/assets/images/extras/deny-icon3.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/extras/email-icon-fill.svg b/assets/images/extras/email-icon-fill.svg new file mode 100644 index 0000000..bc36441 --- /dev/null +++ b/assets/images/extras/email-icon-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/assets/images/extras/icon_check.svg b/assets/images/extras/icon_check.svg new file mode 100644 index 0000000..f16a326 --- /dev/null +++ b/assets/images/extras/icon_check.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/icon_chevron.png b/assets/images/extras/icon_chevron.png new file mode 100644 index 0000000..e9dfe61 Binary files /dev/null and b/assets/images/extras/icon_chevron.png differ diff --git a/assets/images/extras/icon_chevron.svg b/assets/images/extras/icon_chevron.svg new file mode 100644 index 0000000..0c465ff --- /dev/null +++ b/assets/images/extras/icon_chevron.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/icon_chevron_up.png b/assets/images/extras/icon_chevron_up.png new file mode 100644 index 0000000..4a17a26 Binary files /dev/null and b/assets/images/extras/icon_chevron_up.png differ diff --git a/assets/images/extras/icon_chevron_up.svg b/assets/images/extras/icon_chevron_up.svg new file mode 100644 index 0000000..84c4eb4 --- /dev/null +++ b/assets/images/extras/icon_chevron_up.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/icon_logo.png b/assets/images/extras/icon_logo.png new file mode 100644 index 0000000..4c0fa3b Binary files /dev/null and b/assets/images/extras/icon_logo.png differ diff --git a/assets/images/extras/icon_plus.svg b/assets/images/extras/icon_plus.svg new file mode 100644 index 0000000..6c8eca6 --- /dev/null +++ b/assets/images/extras/icon_plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/icon_slider-arrow-left.svg b/assets/images/extras/icon_slider-arrow-left.svg new file mode 100644 index 0000000..67f5145 --- /dev/null +++ b/assets/images/extras/icon_slider-arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/icon_slider-arrow-right.svg b/assets/images/extras/icon_slider-arrow-right.svg new file mode 100644 index 0000000..a3df4dd --- /dev/null +++ b/assets/images/extras/icon_slider-arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/icon_white.svg b/assets/images/extras/icon_white.svg new file mode 100644 index 0000000..d331633 --- /dev/null +++ b/assets/images/extras/icon_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/icon_white_1.svg b/assets/images/extras/icon_white_1.svg new file mode 100644 index 0000000..fc57755 --- /dev/null +++ b/assets/images/extras/icon_white_1.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/image.svg b/assets/images/extras/image.svg new file mode 100644 index 0000000..1f3523f --- /dev/null +++ b/assets/images/extras/image.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/extras/livechat-icon.svg b/assets/images/extras/livechat-icon.svg new file mode 100644 index 0000000..4c0b5b2 --- /dev/null +++ b/assets/images/extras/livechat-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/extras/phone-icon.svg b/assets/images/extras/phone-icon.svg new file mode 100644 index 0000000..5690ac4 --- /dev/null +++ b/assets/images/extras/phone-icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/assets/images/extras/pin-icon.svg b/assets/images/extras/pin-icon.svg new file mode 100644 index 0000000..dc43c10 --- /dev/null +++ b/assets/images/extras/pin-icon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/assets/images/extras/placeholder-image.svg b/assets/images/extras/placeholder-image.svg new file mode 100644 index 0000000..cac5869 --- /dev/null +++ b/assets/images/extras/placeholder-image.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/extras/placeholder-video-thumbnail.svg b/assets/images/extras/placeholder-video-thumbnail.svg new file mode 100644 index 0000000..67406f6 --- /dev/null +++ b/assets/images/extras/placeholder-video-thumbnail.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/extras/web.svg b/assets/images/extras/web.svg new file mode 100644 index 0000000..00edfd7 --- /dev/null +++ b/assets/images/extras/web.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/assets/images/logos/Leon-Pharmacy-Logo-whitebg.png b/assets/images/logos/Leon-Pharmacy-Logo-whitebg.png new file mode 100644 index 0000000..1230ef5 Binary files /dev/null and b/assets/images/logos/Leon-Pharmacy-Logo-whitebg.png differ diff --git a/assets/images/logos/Leon-Pharmacy-Logo.png b/assets/images/logos/Leon-Pharmacy-Logo.png new file mode 100644 index 0000000..991c83d Binary files /dev/null and b/assets/images/logos/Leon-Pharmacy-Logo.png differ diff --git a/assets/images/logos/leon_paharmacy-icon.png b/assets/images/logos/leon_paharmacy-icon.png new file mode 100644 index 0000000..717e7b0 Binary files /dev/null and b/assets/images/logos/leon_paharmacy-icon.png differ diff --git a/assets/images/logos/logo-leon-icon.png b/assets/images/logos/logo-leon-icon.png new file mode 100644 index 0000000..3a98443 Binary files /dev/null and b/assets/images/logos/logo-leon-icon.png differ diff --git a/assets/images/logos/logo-leon.png b/assets/images/logos/logo-leon.png new file mode 100644 index 0000000..edca3b3 Binary files /dev/null and b/assets/images/logos/logo-leon.png differ diff --git a/assets/images/logos/logo-leon.svg b/assets/images/logos/logo-leon.svg new file mode 100644 index 0000000..eb27cdc --- /dev/null +++ b/assets/images/logos/logo-leon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/images/services/Synmed Blisterpack.jpg b/assets/images/services/Synmed Blisterpack.jpg new file mode 100644 index 0000000..bc485d4 Binary files /dev/null and b/assets/images/services/Synmed Blisterpack.jpg differ diff --git a/assets/images/services/icon_black_ailment@4x.png b/assets/images/services/icon_black_ailment@4x.png new file mode 100644 index 0000000..dcf466f Binary files /dev/null and b/assets/images/services/icon_black_ailment@4x.png differ diff --git a/assets/images/services/icon_black_blister-pack@4x.png b/assets/images/services/icon_black_blister-pack@4x.png new file mode 100644 index 0000000..88181b9 Binary files /dev/null and b/assets/images/services/icon_black_blister-pack@4x.png differ diff --git a/assets/images/services/icon_black_diabetes@4x.png b/assets/images/services/icon_black_diabetes@4x.png new file mode 100644 index 0000000..daaae7b Binary files /dev/null and b/assets/images/services/icon_black_diabetes@4x.png differ diff --git a/assets/images/services/icon_black_gta-delivery@4x.png b/assets/images/services/icon_black_gta-delivery@4x.png new file mode 100644 index 0000000..7989887 Binary files /dev/null and b/assets/images/services/icon_black_gta-delivery@4x.png differ diff --git a/assets/images/services/icon_black_injection@4x.png b/assets/images/services/icon_black_injection@4x.png new file mode 100644 index 0000000..e198f55 Binary files /dev/null and b/assets/images/services/icon_black_injection@4x.png differ diff --git a/assets/images/services/icon_black_med-review@4x.png b/assets/images/services/icon_black_med-review@4x.png new file mode 100644 index 0000000..8dffc74 Binary files /dev/null and b/assets/images/services/icon_black_med-review@4x.png differ diff --git a/assets/images/services/icon_white_ailment@4x.png b/assets/images/services/icon_white_ailment@4x.png new file mode 100644 index 0000000..c470ab6 Binary files /dev/null and b/assets/images/services/icon_white_ailment@4x.png differ diff --git a/assets/images/services/icon_white_blister-pack@4x.png b/assets/images/services/icon_white_blister-pack@4x.png new file mode 100644 index 0000000..0d48fae Binary files /dev/null and b/assets/images/services/icon_white_blister-pack@4x.png differ diff --git a/assets/images/services/icon_white_diabetes@4x.png b/assets/images/services/icon_white_diabetes@4x.png new file mode 100644 index 0000000..5fa5ee4 Binary files /dev/null and b/assets/images/services/icon_white_diabetes@4x.png differ diff --git a/assets/images/services/icon_white_gta-delivery@4x.png b/assets/images/services/icon_white_gta-delivery@4x.png new file mode 100644 index 0000000..ab70189 Binary files /dev/null and b/assets/images/services/icon_white_gta-delivery@4x.png differ diff --git a/assets/images/services/icon_white_injecion@4x.png b/assets/images/services/icon_white_injecion@4x.png new file mode 100644 index 0000000..929fc3b Binary files /dev/null and b/assets/images/services/icon_white_injecion@4x.png differ diff --git a/assets/images/services/icon_white_med-review@4x.png b/assets/images/services/icon_white_med-review@4x.png new file mode 100644 index 0000000..1434bc5 Binary files /dev/null and b/assets/images/services/icon_white_med-review@4x.png differ diff --git a/assets/images/services/synmed_machine.jpg b/assets/images/services/synmed_machine.jpg new file mode 100644 index 0000000..d4af760 Binary files /dev/null and b/assets/images/services/synmed_machine.jpg differ diff --git a/assets/js/app.js b/assets/js/app.js new file mode 100644 index 0000000..8d9ab51 --- /dev/null +++ b/assets/js/app.js @@ -0,0 +1,22 @@ +let toggle_menu = document.querySelector('.toggle-menu') +let menu_wrapper = document.querySelector('#menu_wrapper') +let currentURL = window.location.href +let menuLinks = document.querySelectorAll('.menu-item a') +let docBody = document.body + +function toggleMenu() { + toggle_menu.classList.toggle('active') + docBody.classList.toggle('noScroll') + console.log('No Scroll Activated') + menu_wrapper.classList.toggle('expand') +} + +function setActiveMenuLink() { + menuLinks.forEach(function (link) { + if (link.href === currentURL) { + link.classList.add('active-highlight') + } + }) +} + +document.addEventListener('DOMContentLoaded', setActiveMenuLink) diff --git a/blocks/contact-block/contact-block.js b/blocks/contact-block/contact-block.js new file mode 100644 index 0000000..1576369 --- /dev/null +++ b/blocks/contact-block/contact-block.js @@ -0,0 +1,65 @@ + +( function ( blocks, element, blockEditor ) { + const el = element.createElement, + registerBlockType = blocks.registerBlockType, + ServerSideRender = PgServerSideRender2, + InspectorControls = blockEditor.InspectorControls, + useBlockProps = blockEditor.useBlockProps; + + const {__} = wp.i18n; + const {ColorPicker, TextControl, ToggleControl, SelectControl, Panel, PanelBody, Disabled, TextareaControl, BaseControl} = wp.components; + const {useSelect} = wp.data; + const {RawHTML, Fragment} = element; + + const {InnerBlocks, URLInputButton, RichText} = wp.blockEditor; + const useInnerBlocksProps = blockEditor.useInnerBlocksProps || blockEditor.__experimentalUseInnerBlocksProps; + + const propOrDefault = function(val, prop, field) { + if(block.attributes[prop] && (val === null || val === '')) { + return field ? block.attributes[prop].default[field] : block.attributes[prop].default; + } + return val; + } + + const block = registerBlockType( 'leonpharmacy/contact-block', { + apiVersion: 2, + title: 'Contact Page', + description: '', + icon: 'block-default', + category: 'Leon Pharmacy Blocks', + keywords: [], + supports: {color: {background: false,text: false,gradients: false,link: false,},typography: {fontSize: false,},anchor: false,align: false,}, + attributes: { + }, + example: { attributes: { } }, + edit: function ( props ) { + const blockProps = useBlockProps({ className: 'bg-blue-500' }); + const setAttributes = props.setAttributes; + + + const innerBlocksProps = null; + + + return el(Fragment, {}, [ + + el( ServerSideRender, { + block: 'leonpharmacy/contact-block', + httpMethod: 'POST', + attributes: props.attributes, + innerBlocksProps: innerBlocksProps, + blockProps: blockProps + } ), + + ]); + }, + + save: function(props) { + return null; + } + + } ); +} )( + window.wp.blocks, + window.wp.element, + window.wp.blockEditor +); diff --git a/blocks/contact-block/contact-block.php b/blocks/contact-block/contact-block.php new file mode 100644 index 0000000..1a3311e --- /dev/null +++ b/blocks/contact-block/contact-block.php @@ -0,0 +1,85 @@ +
"bg-blue-500", ) ); else echo 'data-wp-block-props="true"'; ?>> +
+
+
+
+

+

+

+
+
+ + process( array( + 'form_id' => 'contact_form_mailer_id', + 'send_to_email' => true, + 'save_to_post_type' => true, + 'email' => 'info@leonpharmacy.com', + 'post_type' => 'contact_message', + 'title' => 'New Message', + 'intro' => 'New Message Received' + ) ); ?> + processed || $mailer->error) : ?> +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + processed ) : ?> + error ) : ?> +
+
+

+
+

+
+
+
+ +
+
+

+
+

+
+
+
+ + +
+
+

+
+
+

+
+
+

+
+
+
+
+
+
+
\ No newline at end of file diff --git a/blocks/contact-block/contact-block_register.php b/blocks/contact-block/contact-block_register.php new file mode 100644 index 0000000..9ad3134 --- /dev/null +++ b/blocks/contact-block/contact-block_register.php @@ -0,0 +1,21 @@ + 'leonpharmacy/contact-block', + 'title' => __( 'Contact Page', 'leonpharmacy' ), + 'category' => 'Leon Pharmacy Blocks', + 'enqueue_editor_style' => get_template_directory_uri() . '/assets/css/tailwind_for_wp_editor.css', + 'render_template' => 'blocks/contact-block/contact-block.php', + 'supports' => array('color' => array('background' => false,'text' => false,'gradients' => false,'link' => false,),'typography' => array('fontSize' => false,),'anchor' => false,'align' => false,), + 'base_url' => get_template_directory_uri(), + 'base_path' => get_template_directory(), + 'js_file' => 'blocks/contact-block/contact-block.js', + 'attributes' => array( + + ), + 'example' => array( + + ), + 'dynamic' => true, + 'version' => '1.5' + ) ); diff --git a/blocks/pg-blocks-controls-2.css b/blocks/pg-blocks-controls-2.css new file mode 100644 index 0000000..840c656 --- /dev/null +++ b/blocks/pg-blocks-controls-2.css @@ -0,0 +1,119 @@ +.pg-posts-control-list { + width: 100%; +} + +.pg-posts-control-list-item-empty { + +} + +.pg-posts-control-list-item { + border-radius: 2px; + background-color: rgb(240, 240, 240); + padding: 8px; + padding-left: 25px; + padding-right: 25px; + text-align: left; + align-items: center; + justify-content: center; + width: 100%; + position: relative; + margin-bottom: 4px; +} + +.pg-posts-control-list-item-remove { + text-decoration: none; + position: absolute; + right: 8px; + font-size: 18px; + line-height: 1; + color: rgba(0,0,0,0.25); + opacity: 0; + transition: all 0.3s; + top: 50%; + margin-top: -10px; +} + +.pg-posts-control-list-item-remove:hover { + color: rgba(0,0,0,0.5); +} + +.pg-posts-control-list-item-move { + position: absolute; + left: 2px; + height: 16px; + top: 50%; + opacity: 0; + transition: all 0.3s; + color: rgba(0,0,0,0.25); + font-size: 15px; + text-decoration: none; +} + +.pg-posts-control-list-item-move:hover { + color: rgba(0,0,0,0.5); +} + +.pg-posts-control-list-item-move.up { + margin-top: -18px; +} + +.pg-posts-control-list-item-move.down { + margin-top: 2px; +} + +.pg-posts-control-list-item:hover .pg-posts-control-list-item-move { + opacity: 1; +} + +.pg-posts-control-list-item:hover .pg-posts-control-list-item-remove { + opacity: 1; +} + +.pg-control-close-popup { + text-decoration: none; + font-size: 22px; + transition: all 0.3s; + color: rgba(0,0,0,0.25); + text-align: right; + padding-right: 4px; + display: block; + line-height: 1; + height: 10px; + z-index: 1; + position: relative; +} + +.pg-control-close-popup:hover { + color: rgba(0,0,0,0.5); +} + +.pg-control-popover { + padding-top:20px; +} + +.pg-control-color-popover .components-popover__content{ + padding: 15px 15px 5px 15px !important; +} + +.pg-control-color-popover .pg-control-close-popup { + padding-right: 0; + margin-left: auto; + position: absolute; + right: -13px; + top: -19px; +} + +.pg-control-svg-edit .components-popover__content { + padding: 10px 10px 10px 10px !important; +} + +.pg-control-svg-edit-code textarea { + min-width: 300px; + min-height: 120px; + font-family: Menlo,Consolas,monaco,monospace; +} + +.pg-control-svg-edit-preview svg { + max-height: 100px; + max-width: 100%; +} diff --git a/blocks/pg-blocks-controls-2.js b/blocks/pg-blocks-controls-2.js new file mode 100644 index 0000000..468c054 --- /dev/null +++ b/blocks/pg-blocks-controls-2.js @@ -0,0 +1,1051 @@ +function pgPostSelectorControl(prop, setAttributes, props, title, help, post_type) { + const {__} = wp.i18n; + const el = wp.element.createElement; + const {Button, Popover, BaseControl, Panel, PanelBody, PanelRow} = wp.components; + const {useState} = wp.element; + const LinkControl = wp.blockEditor.LinkControl || wp.blockEditor.__experimentalLinkControl; + + const postId = prop + 'Id'; + + title = title || __('Select ' + prop); + + let [isVisible, setIsVisible] = useState(false); + const toggleVisible = () => { + setIsVisible((state) => !state); + }; + + const findItemById = function (id) { + if (props.attributes[prop]) { + for (let i = 0; i < props.attributes[prop].length; i++) { + if (props.attributes[prop][i].id === id) return i; + } + } + return -1; + } + + const moveItem = function (id, add) { + const idx = findItemById(id); + if (idx >= 0) { + if (idx + add >= 0 && (idx + add) < props.attributes[prop].length) { + const list = props.attributes[prop].slice(0); + const mi = list[idx]; + list.splice(idx, 1); + list.splice(idx + add, 0, mi) + const d = {} + d[prop] = list; + setAttributes(d); + } + } + } + + const renderItems = function () { + var r = []; + if (props.attributes[prop]) { + let i = 0; + props.attributes[prop].forEach(function (item) { + r.push(el('div', { + className: 'pg-posts-control-list-item', + }, [ + props[prop + '_' + item.id] ? props[prop + '_' + item.id].title.raw : 'Loading...', + el('a', { + href: '#', + className: 'pg-posts-control-list-item-remove', + onClick: function () { + const idx = findItemById(item.id); + if (idx >= 0) { + const list = props.attributes[prop].slice(0); + list.splice(idx, 1); + const d = {} + d[prop] = list; + setAttributes(d); + } + } + }, '×'), + el('a', { + href: '#', + className: 'pg-posts-control-list-item-move up dashicons dashicons-arrow-up-alt2', + onClick: function () { + moveItem(item.id, -1); + } + }, ''), + el('a', { + href: '#', + className: 'pg-posts-control-list-item-move down dashicons dashicons-arrow-down-alt2', + onClick: function () { + moveItem(item.id, 1); + } + }, '') + ])) + i++; + }) + } + return r; + } + + return el(Panel, {}, el(PanelBody, { + title: __(title) + }, [ + el(BaseControl, { + help: help && __(help) + }, [ + el(PanelRow, {}, + el('div', { + className: 'pg-posts-control-list' + }, [ + props.attributes[prop].length === 0 && el('div', { + className: 'pg-posts-control-list-item-empty' + }, [__('No posts selected')]), + props.attributes[prop].length > 0 && renderItems() + ]) + ), + el(PanelRow, {}, [ + el(Button, { + onClick: toggleVisible, + isSecondary: true + }, [ + __('Select a ' + post_type), + ]) + ]) + ]), + isVisible && el(Popover, { + className: 'pg-control-popover' + }, [ + el('a', { + href: '#', + tabIndex: '-1', + className: 'pg-control-close-popup', + onClick: function () { + setIsVisible(false); + }, + }, [ + '×' + ]), + el(LinkControl, { + onChange: function (val, p) { + if (findItemById(val.id) < 0) { + var d = {}; + d[prop] = props.attributes[prop].slice(0); + d[prop].push({id: val.id}); + setAttributes(d); + } + setIsVisible(false); + }, + noDirectEntry: true, + searchInputPlaceholder: __('Select a post'), + showInitialSuggestions: true, + settings: [], + hasRichPreviews: true, + suggestionsQuery: post_type ? { + type: 'post', + subtype: post_type, + } : {} + }), + ]) + ])) +} + +function pgMediaImageControl(prop, setAttributes, props, image_size, inline_svg, title, help) { + const propMedia = prop; + + const {__} = wp.i18n; + const el = window.wp.element.createElement; + const { + BaseControl, + Button, + ResponsiveWrapper, + Panel, + PanelBody, + PanelRow, + TextareaControl, + Popover + } = wp.components; + const {MediaUploadCheck, MediaUpload} = wp.blockEditor; + const {RawHTML, useState} = wp.element; + const ResolutionTool = wp.blockEditor?.privateApis?.ResolutionTool; + const ImageSizeControl = ResolutionTool ? null : (wp.blockEditor.ImageSizeControl || wp.blockEditor.__experimentalImageSizeControl); + + image_size = image_size || 'full'; + + let [isEditSVGVisible, setIsEditSVGVisible] = useState({ + visible: false, + value: '' + }); + + function onSelectMedia(media) { + var d = {} + d[prop] = { + id: media.id, + url: media?.sizes[image_size]?.url || media.url, + size: image_size, + svg: '', + alt: media.alt || '' + } + setAttributes(d); + } + + function setSVG(svg) { + var d = {} + d[prop] = { + id: 0, + url: '', + size: '', + svg: inline_svg ? svg : '', + alt: '' + } + setAttributes(d); + } + + function removeMedia() { + var d = {} + d[prop] = { + id: 0, + url: '', + size: '', + svg: '', + alt: '' + } + setAttributes(d); + } + + function selectMediaSize(url, size) { + var d = {}; + d[prop] = { + id: props[prop].id, + url: url, + size: size, + svg: '', + alt: props[prop].alt || '' + } + setAttributes(d); + } + + function getId() { + return props.attributes[prop].id || 0; + } + + function getUrl() { + return props.attributes[prop].url || ''; + } + + function getAlt() { + return props.attributes[prop].alt || ''; + } + + function getSVG() { + if (!inline_svg) return ''; + return props.attributes[prop].svg || ''; + } + + function getResolutionOptions() { + var r = []; + Object.keys(props[propMedia].media_details.sizes).forEach(function (k) { + r.push({ + value: k, + label: `${k} (${props[propMedia].media_details.sizes[k].width}x${props[propMedia].media_details.sizes[k].height})` + }) + }); + return r; + } + + return el(Panel, {}, el(PanelBody, { + title: __(`${title || prop}`) + }, [ + el(PanelRow, {}, [ + el(MediaUploadCheck, {}, [ + el(MediaUpload, { + onSelect: onSelectMedia, + value: getId(), + allowedTypes: ['image'], + render: (open) => el(Button, { + className: getUrl() === '' ? 'editor-post-featured-image__toggle' : 'editor-post-featured-image__preview', + style: { + maxHeight: '200px', + overflow: 'hidden' + }, + onClick: open.open + }, [ + (getUrl() === '' && getSVG() === '') && __('Select an image'), + !getSVG() && props[propMedia] && props[propMedia].source_url && + el(ResponsiveWrapper, { + naturalWidth: props[propMedia].media_details.width, + naturalHeight: props[propMedia].media_details.height, + isInline: true + }, + el('img', { + src: props[propMedia].source_url + }) + ), + getId() === 0 && getUrl() && el('img', { + src: getUrl(), + style: { + width: '100%', + display: 'block', + objectFit: 'cover', + height: '100%', + objectPosition: 'center center' + } + }), + inline_svg && getSVG() && el('div', { + className: 'pg-control-svg-edit-preview' + }, el(RawHTML, {}, getSVG())) + ]) + + }, []), + ]), + ]), + + inline_svg && !getUrl() && !getSVG() && el(PanelRow, {}, [ + el(Button, { + onClick: function () { + setIsEditSVGVisible({visible: true, value: getSVG()}); + }, + style: { + marginRight: '4px', + }, + isLink: true + }, [ + __('Set inline SVG') + ]), + ]), + + (getUrl() || getSVG()) && el(PanelRow, {}, [ + el(MediaUploadCheck, {}, [ + el(MediaUpload, { + title: __('Replace image'), + onSelect: onSelectMedia, + value: getId(), + allowedTypes: ['image'], + render: (open) => el(Button, { + onClick: open.open, + isSecondary: true + }, [ + __('Replace image'), + ]) + + }, []), + ]), + inline_svg && el(Button, { + onClick: function () { + setIsEditSVGVisible({visible: true, value: getSVG()}); + }, + style: { + marginRight: '6px', + marginLeft: '6px' + }, + isSecondary: true + }, [ + __('Edit SVG') + ]), + el(Button, { + onClick: removeMedia, + isLink: true, + isDestructive: false + }, [ + __('Remove image') + ]) + ]), + + getId() !== 0 && props[propMedia] && pgImageSizeControl(prop, setAttributes, props, getResolutionOptions(), function (value) { + selectMediaSize(props[propMedia].media_details.sizes[value].source_url, value); + }), + + isEditSVGVisible.visible && el(Popover, { + className: 'pg-control-svg-edit', + style: {} + }, [ + el(BaseControl, {}, [ + el(TextareaControl, { + value: getSVG(), + help: __('Paste or edit the SVG image code.'), + label: __('Edit inline SVG'), + className: 'pg-control-svg-edit-code', + onChange: function (val) { + setSVG(val); + }, + }), + ]), + el(Button, { + onClick: function () { + setIsEditSVGVisible({...isEditSVGVisible, visible: false}); + }, + style: { + marginRight: '6px' + }, + isSecondary: true + }, [ + __('Close') + ]), + el(Button, { + onClick: function () { + setSVG(isEditSVGVisible.value); + setIsEditSVGVisible({...isEditSVGVisible, visible: false}); + }, + isLink: true + }, [ + __('Cancel') + ]), + ]), + help && el(BaseControl, { + help: __(help), + }, []) + ])) +} + +function pgColorControl(prop, setAttributes, props, title, help) { + const {__} = wp.i18n; + const el = wp.element.createElement; + const {Button, Popover, BaseControl, ColorPicker, PanelRow} = wp.components; + const {useState} = wp.element; + + let [isVisible, setIsVisible] = useState(false); + + const toggleVisible = () => { + setIsVisible((state) => !state); + }; + + return el(BaseControl, { + help: help && __(help) + }, [ + el(PanelRow, {}, [ + el(Button, { + onClick: toggleVisible, + isSecondary: true + }, [ + el('span', {}, __(title || 'Select color')), + el('span', { + style: { + backgroundColor: props.attributes[prop], + border: '1px solid rgba(0,0,0,0.15)', + width: '20px', + height: '20px', + borderRadius: '50%', + marginLeft: '8px' + } + }) + ]), + props.attributes[prop] !== '' && el(Button, { + onClick: function () { + var d = {} + d[prop] = ''; + setAttributes(d); + }, + isLink: true, + isDestructive: false + }, [ + __('Clear') + ]), + isVisible && el(Popover, { + className: 'pg-control-color-popover' + }, [ + el('a', { + href: '#', + tabIndex: -1, + className: 'pg-control-close-popup', + onClick: function () { + setIsVisible(false); + }, + }, [ + '×' + ]), + el(ColorPicker, { + color: props.attributes[prop], + label: __(prop), + onChangeComplete: function (value) { + var d = {} + d[prop] = value.color ? value.color.toRgbString() : ('rgba(' + value.rgb.r + ',' + value.rgb.g + ',' + value.rgb.b + ',' + value.rgb.a + ')'); + setAttributes(d); + } + }), + ]) + ]), + ]) +} + +function pgUrlControl(prop, setAttributes, props, title, help, post_type) { + const {__} = wp.i18n; + const el = wp.element.createElement; + const {Button, Popover, BaseControl, Panel, PanelBody, PanelRow, SelectControl, Icon} = wp.components; + const {useState} = wp.element; + const LinkControl = wp.blockEditor.LinkControl || wp.blockEditor.__experimentalLinkControl; + const {withSelect, useSelect} = wp.data; + + title = title || prop; + + let linkControlKey = 1; + + let [isVisible, setIsVisible] = useState(false); + const toggleVisible = () => { + setIsVisible((state) => !state); + }; + + const getPostTypeOptions = useSelect((select) => { + const {getPostTypes} = select('core'); + + const items = (getPostTypes() || []).filter(function (item) { + return !item.slug.startsWith('wp_'); + }); + + const options = items.map(function (item) { + return { + label: item.labels.name, + value: item.slug, + }; + }); + + options.unshift({value: null, label: '-'}); + return options; + }); + + return el(BaseControl, { + help: help && __(help), + label: __(title || prop) + }, [ + el(PanelRow, {}, [ + el(Button, { + onClick: toggleVisible, + isSecondary: true, + style: { + flexGrow: 1, + marginRight: '8px' + } + }, [ + el(Icon, {icon: 'admin-links', style: {marginRight: '4px'}}), + !props.attributes[prop].url && el('span', {}, __('Select link')), + props.attributes[prop].url && el('span', { + style: { + maxWidth: '170px', + display: 'inline-block', + textOverflow: 'ellipsis', + overflow: 'hidden' + } + }, props.attributes[prop].title || props.attributes[prop].url), + props.attributes[prop].url && el(Button, { + onClick: function (e) { + e.preventDefault(); + e.stopPropagation(); + + var d = {} + d[prop] = { + post_type: props.attributes[prop].post_type || null, + post_id: 0, + url: '' + }; + setAttributes(d); + }, + isLink: true, + isDestructive: false, + style: { + fontSize: '16px', + textDecoration: 'none', + marginLeft: 'auto' + } + }, [ + __('×') + ]), + ]), + + ]), + isVisible && el(Popover, {}, [ + el('div', { + style: { + margin: '0 16px 16px 16px' + } + }, [ + el('a', { + href: '#', + style: { + right: '-15px' + }, + tabIndex: -1, + className: 'pg-control-close-popup', + onClick: function () { + setIsVisible(false); + }, + }, [ + '×' + ]), + el(SelectControl, { + value: props.attributes[prop].post_type, + label: __('Post type'), + disabled: post_type !== null, + onChange: function (val) { + var d = {} + d[prop] = { + post_type: val === '-' ? null : val, + post_id: 0, + url: '' + }; + linkControlKey++; + //setIsVisible(false); + setAttributes(d) + }, + options: getPostTypeOptions + }), + ]), + el(LinkControl, { + key: props.attributes[prop].post_type || 'none', + value: { + url: props.attributes[prop].url + }, + onChange: function (val) { + var d = {}; + d[prop] = { + post_type: props.attributes[prop].post_type, + post_id: val.id, + url: val.url, + title: val.title + }; + if (val.url === props.attributes[prop].url && !val.id) { + d[prop].post_id = props.attributes[prop].post_id; + d[prop].title = props.attributes[prop].title; + } + setIsVisible(false); + setAttributes(d) + }, + noDirectEntry: false, + searchInputPlaceholder: __('Select a post'), + showInitialSuggestions: true, + forceIsEditingLink: true, + settings: [], + suggestionsQuery: props.attributes[prop].post_type ? { + type: 'post', + subtype: props.attributes[prop].post_type, + } : {} + }), + ]) + ]) + +} + +function pgImageSizeControl(prop, setAttributes, props, options, onChange ) { + const {__} = wp.i18n; + const el = wp.element.createElement; + const {ToolsPanelItem, Button, Popover, BaseControl, Panel, PanelBody, PanelRow, SelectControl, Icon} = wp.components; + + const title = __('Image size'); + + return el(PanelRow, {}, [el(BaseControl, { + }, [ + el(SelectControl, { + label: title, + value: props.attributes[prop].size, + options: options, + onChange: function(value) { + onChange(value) + } + }) + ]) + ]) +} + +function pgMergeInlineSVGAttributes(svg, props) { + for (let prop in props) { + let val = props[prop]; + if (typeof val === 'object') { + const r = []; + for (const key in val) { + r.push(`${key.replace(/([A-Z])/g, function (a) { + return '-' + a.toLowerCase(); + })}:${val[key]};`) + } + val = r.join(''); + } + if (prop === 'className') prop = 'class'; + const re = new RegExp('(]*\\s*)(' + prop + '="[^"]*")', 'i'); + if (svg.match(re)) { + svg = svg.replace(re, '$1' + prop + '="' + val + '"'); + } else { + svg = svg.replace(/ 1) { + const sp = b.shift().trim().replace(/-([a-z])/gi, function (s, g) { + return g.toUpperCase(); + }) + styles[sp] = b.join(':').trim(); + } + }) + props.style = styles; + } else { + props[name] = node.attributes[i].value === null ? '' : node.attributes[i].value; + } + } + return props; + } + + removeScripts(node) { + var scripts = node.querySelectorAll('script'); + for (let i = 0; i < scripts.length; i++) { + scripts[i].parentNode.removeChild(scripts[i]); + } + } + + render(el, lib) { + const _this = this; + + el = el || window.wp.element.createElement; + lib = lib || {} + + const doNode = function (node) { + if (node.nodeType === 1) { + + let tag = node.tagName.toLowerCase(); + const props = _this.attributesToProps(node); + + const children = []; + let skip_children = false; + + if (tag === 'svg') { + props.dangerouslySetInnerHTML = {__html: node.innerHTML} + skip_children = true; + } + + if (!skip_children) { + for (let i = 0; i < node.childNodes.length; i++) { + const ch = doNode(node.childNodes[i]); + ch && children.push(ch); + } + } + if (lib[tag]) { + tag = lib[tag]; + } + + if (children.length) { + return el(tag, props, children); + } else { + return el(tag, props); + } + } else if (node.nodeType === 3) { + return node.data; + } + } + return doNode(this.doc.querySelector('body').children[0]); + } +} + +(function() { + + const pgCreateSVGCache = {} + + function pgCreateSVG(dummytag, dummyprops, svg) { + const pgReact = new PgHTMLToReact2(); + const el = window.wp.element.createElement; + + let props = pgCreateSVGCache[svg] || null; + if (!props) { + const parser = new DOMParser(); + const doc = parser.parseFromString(svg, "image/svg+xml"); + + const svgel = doc.querySelector('svg'); + pgReact.removeScripts(doc); + pgReact.cleanNode(svgel); + + props = pgReact.attributesToProps(svgel); + + props.dangerouslySetInnerHTML = {__html: svgel.innerHTML} + pgCreateSVGCache[svg] = props; + } + return el('svg', props); + } + + window.pgCreateSVG = pgCreateSVG; +})() + +function PgGetServerSideRender2() { + //Addapted ServerSideRender that properly supports InnerBlocks + const {useDebounce, usePrevious} = wp.compose; + const {RawHTML, useEffect, useRef, useState, Fragment} = wp.element; + const {__, sprintf} = wp.i18n; + const apiFetch = wp.apiFetch; + const {addQueryArgs} = wp.url; + const {Placeholder, Spinner} = wp.components; + const {InnerBlocks} = wp.blockEditor; + const SanitizeBlockAttributes = wp.blocks.SanitizeBlockAttributes || wp.blocks.__experimentalSanitizeBlockAttributes; + const useInnerBlocksProps = wp.blockEditor.useInnerBlocksProps || wp.blockEditor.__experimentalUseInnerBlocksProps; + const el = wp.element.createElement; + + //Addapted from fast-deep-equal + function equal(a, b) { + if (a === b) return true; + + if (a && b && typeof a == 'object' && typeof b == 'object') { + if (a.constructor !== b.constructor) return false; + + var length, i, keys; + if (Array.isArray(a)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (!equal(a[i], b[i])) return false; + return true; + } + + if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; + if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); + if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); + + keys = Object.keys(a); + length = keys.length; + if (length !== Object.keys(b).length) return false; + + for (i = length; i-- !== 0;) + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + + for (i = length; i-- !== 0;) { + var key = keys[i]; + + if (key === '_owner' && a.$$typeof) { + continue; + } + if (!equal(a[key], b[key])) return false; + } + + return true; + } + return a !== a && b !== b; + } + + function rendererPath(block, attributes = null, urlQueryArgs = {}) { + return addQueryArgs(`/wp/v2/block-renderer/${block}`, { + context: 'edit', + ...(null !== attributes ? {attributes} : {}), + ...urlQueryArgs, + }); + } + + function DefaultEmptyResponsePlaceholder({className}) { + return el(Placeholder, {className: className}, __('Block rendered as empty.')); + } + + function DefaultErrorResponsePlaceholder({response, className}) { + const errorMessage = sprintf( + // translators: %s: error message describing the problem + __('Error loading block: %s'), + response.errorMsg + ); + return el(Placeholder, {className: className}, errorMessage); + } + + function DefaultLoadingResponsePlaceholder({className}) { + return el(Placeholder, {className: className}, el(Spinner, {})); + } + + + return function (props) { + const { + attributes, + block, + className, + httpMethod = 'GET', + urlQueryArgs, + EmptyResponsePlaceholder = DefaultEmptyResponsePlaceholder, + ErrorResponsePlaceholder = DefaultErrorResponsePlaceholder, + LoadingResponsePlaceholder = DefaultLoadingResponsePlaceholder, + innerBlocksProps, + blockProps + } = props; + + const isMountedRef = useRef(true); + const fetchRequestRef = useRef(); + const [response, setResponse] = useState(null); + const prevProps = usePrevious(props); + + const innerAttribute = 'data-wp-inner-blocks'; + const blocksPropsAttribute = 'data-wp-block-props'; + + let currentCode = null; + let currentFunction = null; + + function pgel(tag, props, ...children) { + if (tag === 'script') return null; + + if (props) { + if (props.class) { + props.className = props.class; + delete props.class; + } + + for (const prop in props) { + if (props.hasOwnProperty(prop)) { + if (prop.match(/^on[A-Z]+/)) { + props[prop] = null; + } + } + } + if (innerAttribute in props) { + delete props[innerAttribute]; + props = {...props, ...innerBlocksProps}; + return el(tag, props); + } + if (blocksPropsAttribute in props) { + delete props[blocksPropsAttribute]; + props = {...props, ...blockProps}; + } + } + if (children.length) { + return el(tag, props, children); + } else { + return el(tag, props); + } + } + + function fetchData() { + isMountedRef.needsFetch = false; + + if (!isMountedRef.current) { + return; + } + if (null !== response) { + setResponse(null); + } + + const sanitizedAttributes = + attributes && + SanitizeBlockAttributes(block, attributes); + + // If httpMethod is 'POST', send the attributes in the request body instead of the URL. + // This allows sending a larger attributes object than in a GET request, where the attributes are in the URL. + const isPostRequest = 'POST' === httpMethod; + const urlAttributes = isPostRequest + ? null + : sanitizedAttributes ?? null; + const path = rendererPath(block, urlAttributes, urlQueryArgs); + const data = isPostRequest + ? {attributes: sanitizedAttributes ?? null} + : null; + + // Store the latest fetch request so that when we process it, we can + // check if it is the current request, to avoid race conditions on slow networks. + const fetchRequest = (fetchRequestRef.current = apiFetch({ + path, + data, + method: isPostRequest ? 'POST' : 'GET', + }) + .then((fetchResponse) => { + if ( + isMountedRef.current && + fetchRequest === fetchRequestRef.current && + fetchResponse + ) { + setResponse(fetchResponse.rendered); + } + }) + .catch((error) => { + if ( + isMountedRef.current && + fetchRequest === fetchRequestRef.current + ) { + setResponse({ + error: true, + errorMsg: error.message, + }); + } + })); + + return fetchRequest; + } + + const debouncedFetchData = useDebounce(fetchData, 500); + + // When the component unmounts, set isMountedRef to false. This will + // let the async fetch callbacks know when to stop. + useEffect( + () => () => { + isMountedRef.current = false; + isMountedRef.needsFetch = false; + }, + [] + ); + + useEffect(() => { + // Don't debounce the first fetch. This ensures that the first render + // shows data as soon as possible + if (prevProps === undefined) { + fetchData(); + } else if (!equal(prevProps.attributes, props.attributes)) { + isMountedRef.needsFetch = true; + debouncedFetchData(); + } else { + if (isMountedRef.needsFetch) { + fetchData(); + } + } + }); + + if (response === '') { + return el(EmptyResponsePlaceholder, {...props, ...blockProps}); + } else if (!response) { + return el(LoadingResponsePlaceholder, {...props, ...blockProps}); + } else if (response.error) { + return el(ErrorResponsePlaceholder, {response: response, ...props, ...blockProps}); + } + + try { + + if (isMountedRef.currentCode !== response) { + isMountedRef.currentFunction = new PgHTMLToReact2(); + isMountedRef.currentFunction.loadHTML(response); + isMountedRef.currentCode = response; + } + return isMountedRef.currentFunction.render(pgel); + } catch (err) { + //unable to render inline + console.warn('Unable to compile dynamic block to JSX:', err); + return el('div', {...blockProps}, [ + el(RawHTML, {className: className}, response), + el('div', {...innerBlocksProps}) + ]) + } + } + +} + +window.PgServerSideRender2 = PgGetServerSideRender2(); diff --git a/blocks/sidebar-heading/sidebar-heading.js b/blocks/sidebar-heading/sidebar-heading.js new file mode 100644 index 0000000..d880014 --- /dev/null +++ b/blocks/sidebar-heading/sidebar-heading.js @@ -0,0 +1,83 @@ + +( function ( blocks, element, blockEditor ) { + const el = element.createElement, + registerBlockType = blocks.registerBlockType, + ServerSideRender = PgServerSideRender2, + InspectorControls = blockEditor.InspectorControls, + useBlockProps = blockEditor.useBlockProps; + + const {__} = wp.i18n; + const {ColorPicker, TextControl, ToggleControl, SelectControl, Panel, PanelBody, Disabled, TextareaControl, BaseControl} = wp.components; + const {useSelect} = wp.data; + const {RawHTML, Fragment} = element; + + const {InnerBlocks, URLInputButton, RichText} = wp.blockEditor; + const useInnerBlocksProps = blockEditor.useInnerBlocksProps || blockEditor.__experimentalUseInnerBlocksProps; + + const propOrDefault = function(val, prop, field) { + if(block.attributes[prop] && (val === null || val === '')) { + return field ? block.attributes[prop].default[field] : block.attributes[prop].default; + } + return val; + } + + const block = registerBlockType( 'leonpharmacy/sidebar-heading', { + apiVersion: 2, + title: 'Sidebar Heading', + description: '', + icon: 'block-default', + category: 'archive', + keywords: [], + supports: {color: {background: false,text: false,gradients: false,link: false,},typography: {fontSize: false,},anchor: false,align: false,}, + attributes: { + heading: { + type: 'string', + default: `Heading`, + } + }, + example: { attributes: { heading: `Heading` } }, + edit: function ( props ) { + const blockProps = useBlockProps({ className: 'mb-4' }); + const setAttributes = props.setAttributes; + + + const innerBlocksProps = null; + + + return el(Fragment, {}, [ + el('div', { ...blockProps }, [' ', el(RichText, { tagName: 'h2', className: 'font-bold mb-4 text-2xl', value: propOrDefault( props.attributes.heading, 'heading' ), onChange: function(val) { setAttributes( {heading: val }) }, withoutInteractiveFormatting: true, allowedFormats: [] }), ' ', el('hr', { className: 'mb-4' }), ' ']), + + el( InspectorControls, {}, + [ + + el(Panel, {}, + el(PanelBody, { + title: __('Block properties') + }, [ + + el(TextControl, { + value: props.attributes.heading, + help: __( '' ), + label: __( 'Heading' ), + onChange: function(val) { setAttributes({heading: val}) }, + type: 'text' + }), + ]) + ) + ] + ) + + ]); + }, + + save: function(props) { + const blockProps = useBlockProps.save({ className: 'mb-4' }); + return el('div', { ...blockProps }, [' ', el(RichText.Content, { tagName: 'h2', className: 'font-bold mb-4 text-2xl', value: propOrDefault( props.attributes.heading, 'heading' ) }), ' ', el('hr', { className: 'mb-4' }), ' ']); + } + + } ); +} )( + window.wp.blocks, + window.wp.element, + window.wp.blockEditor +); diff --git a/blocks/sidebar-heading/sidebar-heading_register.php b/blocks/sidebar-heading/sidebar-heading_register.php new file mode 100644 index 0000000..b000d6b --- /dev/null +++ b/blocks/sidebar-heading/sidebar-heading_register.php @@ -0,0 +1,22 @@ + 'leonpharmacy/sidebar-heading', + 'title' => __( 'Sidebar Heading', 'leonpharmacy' ), + 'category' => 'archive', + 'supports' => array('color' => array('background' => false,'text' => false,'gradients' => false,'link' => false,),'typography' => array('fontSize' => false,),'anchor' => false,'align' => false,), + 'base_url' => get_template_directory_uri(), + 'base_path' => get_template_directory(), + 'js_file' => 'blocks/sidebar-heading/sidebar-heading.js', + 'attributes' => array( + 'heading' => array( + 'type' => 'string', + 'default' => 'Heading' + ) + ), + 'example' => array( +'heading' => 'Heading' + ), + 'dynamic' => false, + 'version' => '1.5' + ) ); diff --git a/category.php b/category.php new file mode 100644 index 0000000..6d9ace4 --- /dev/null +++ b/category.php @@ -0,0 +1,79 @@ + + +
+
+
+

+
+
+
+
+
+
+ + + + +
+
+
+ + \ No newline at end of file diff --git a/class-tgm-plugin-activation.php b/class-tgm-plugin-activation.php new file mode 100644 index 0000000..22ec0f2 --- /dev/null +++ b/class-tgm-plugin-activation.php @@ -0,0 +1,4030 @@ +wp_version = $GLOBALS['wp_version']; + + // Announce that the class is ready, and pass the object (for advanced use). + do_action_ref_array('tgmpa_init', array($this)); + + /* + * Load our text domain and allow for overloading the fall-back file. + * + * {@internal IMPORTANT! If this code changes, review the regex in the custom TGMPA + * generator on the website.}} + */ + add_action('init', array($this, 'load_textdomain'), 5); + add_filter('load_textdomain_mofile', array($this, 'overload_textdomain_mofile'), 10, 2); + + // When the rest of WP has loaded, kick-start the rest of the class. + add_action('init', array($this, 'init')); + } + + /** + * Magic method to (not) set protected properties from outside of this class. + * + * {@internal hackedihack... There is a serious bug in v2.3.2 - 2.3.6 where the `menu` property + * is being assigned rather than tested in a conditional, effectively rendering it useless. + * This 'hack' prevents this from happening.}} + * + * @see https://github.com/TGMPA/TGM-Plugin-Activation/blob/2.3.6/tgm-plugin-activation/class-tgm-plugin-activation.php#L1593 + * + * @since 2.5.2 + * + * @param string $name Name of an inaccessible property. + * @param mixed $value Value to assign to the property. + * @return void Silently fail to set the property when this is tried from outside of this class context. + * (Inside this class context, the __set() method if not used as there is direct access.) + */ + public function __set($name, $value) + { + // phpcs:ignore Squiz.PHP.NonExecutableCode.ReturnNotRequired -- See explanation above. + return; + } + + /** + * Magic method to get the value of a protected property outside of this class context. + * + * @since 2.5.2 + * + * @param string $name Name of an inaccessible property. + * @return mixed The property value. + */ + public function __get($name) + { + return $this->{$name}; + } + + /** + * Initialise the interactions between this class and WordPress. + * + * Hooks in three new methods for the class: admin_menu, notices and styles. + * + * @since 2.0.0 + * + * @see TGM_Plugin_Activation::admin_menu() + * @see TGM_Plugin_Activation::notices() + * @see TGM_Plugin_Activation::styles() + */ + public function init() + { + /** + * By default TGMPA only loads on the WP back-end and not in an Ajax call. Using this filter + * you can overrule that behaviour. + * + * @since 2.5.0 + * + * @param bool $load Whether or not TGMPA should load. + * Defaults to the return of `is_admin() && ! defined( 'DOING_AJAX' )`. + */ + if (true !== apply_filters('tgmpa_load', (is_admin() && !defined('DOING_AJAX')))) { + return; + } + + // Load class strings. + $this->strings = array( + 'page_title' => __('Install Required Plugins', 'tgmpa'), + 'menu_title' => __('Install Plugins', 'tgmpa'), + /* translators: %s: plugin name. */ + 'installing' => __('Installing Plugin: %s', 'tgmpa'), + /* translators: %s: plugin name. */ + 'updating' => __('Updating Plugin: %s', 'tgmpa'), + 'oops' => __('Something went wrong with the plugin API.', 'tgmpa'), + /* translators: 1: plugin name(s). */ + 'notice_can_install_required' => _n_noop( + 'This theme requires the following plugin: %1$s.', + 'This theme requires the following plugins: %1$s.', + 'tgmpa' + ), + /* translators: 1: plugin name(s). */ + 'notice_can_install_recommended' => _n_noop( + 'This theme recommends the following plugin: %1$s.', + 'This theme recommends the following plugins: %1$s.', + 'tgmpa' + ), + /* translators: 1: plugin name(s). */ + 'notice_ask_to_update' => _n_noop( + 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.', + 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.', + 'tgmpa' + ), + /* translators: 1: plugin name(s). */ + 'notice_ask_to_update_maybe' => _n_noop( + 'There is an update available for: %1$s.', + 'There are updates available for the following plugins: %1$s.', + 'tgmpa' + ), + /* translators: 1: plugin name(s). */ + 'notice_can_activate_required' => _n_noop( + 'The following required plugin is currently inactive: %1$s.', + 'The following required plugins are currently inactive: %1$s.', + 'tgmpa' + ), + /* translators: 1: plugin name(s). */ + 'notice_can_activate_recommended' => _n_noop( + 'The following recommended plugin is currently inactive: %1$s.', + 'The following recommended plugins are currently inactive: %1$s.', + 'tgmpa' + ), + 'install_link' => _n_noop( + 'Begin installing plugin', + 'Begin installing plugins', + 'tgmpa' + ), + 'update_link' => _n_noop( + 'Begin updating plugin', + 'Begin updating plugins', + 'tgmpa' + ), + 'activate_link' => _n_noop( + 'Begin activating plugin', + 'Begin activating plugins', + 'tgmpa' + ), + 'return' => __('Return to Required Plugins Installer', 'tgmpa'), + 'dashboard' => __('Return to the Dashboard', 'tgmpa'), + 'plugin_activated' => __('Plugin activated successfully.', 'tgmpa'), + 'activated_successfully' => __('The following plugin was activated successfully:', 'tgmpa'), + /* translators: 1: plugin name. */ + 'plugin_already_active' => __('No action taken. Plugin %1$s was already active.', 'tgmpa'), + /* translators: 1: plugin name. */ + 'plugin_needs_higher_version' => __('Plugin not activated. A higher version of %s is needed for this theme. Please update the plugin.', 'tgmpa'), + /* translators: 1: dashboard link. */ + 'complete' => __('All plugins installed and activated successfully. %1$s', 'tgmpa'), + 'dismiss' => __('Dismiss this notice', 'tgmpa'), + 'notice_cannot_install_activate' => __('There are one or more required or recommended plugins to install, update or activate.', 'tgmpa'), + 'contact_admin' => __('Please contact the administrator of this site for help.', 'tgmpa'), + ); + + do_action('tgmpa_register'); + + /* After this point, the plugins should be registered and the configuration set. */ + + // Proceed only if we have plugins to handle. + if (empty($this->plugins) || !is_array($this->plugins)) { + return; + } + + // Set up the menu and notices if we still have outstanding actions. + if (true !== $this->is_tgmpa_complete()) { + // Sort the plugins. + array_multisort($this->sort_order, SORT_ASC, $this->plugins); + + add_action('admin_menu', array($this, 'admin_menu')); + add_action('admin_head', array($this, 'dismiss')); + + // Prevent the normal links from showing underneath a single install/update page. + add_filter('install_plugin_complete_actions', array($this, 'actions')); + add_filter('update_plugin_complete_actions', array($this, 'actions')); + + if ($this->has_notices) { + add_action('admin_notices', array($this, 'notices')); + add_action('admin_init', array($this, 'admin_init'), 1); + add_action('admin_enqueue_scripts', array($this, 'thickbox')); + } + } + + // If needed, filter plugin action links. + add_action('load-plugins.php', array($this, 'add_plugin_action_link_filters'), 1); + + // Make sure things get reset on switch theme. + add_action('switch_theme', array($this, 'flush_plugins_cache')); + + if ($this->has_notices) { + add_action('switch_theme', array($this, 'update_dismiss')); + } + + // Setup the force activation hook. + if (true === $this->has_forced_activation) { + add_action('admin_init', array($this, 'force_activation')); + } + + // Setup the force deactivation hook. + if (true === $this->has_forced_deactivation) { + add_action('switch_theme', array($this, 'force_deactivation')); + } + + // Add CSS for the TGMPA admin page. + add_action('admin_head', array($this, 'admin_css')); + } + + /** + * Load translations. + * + * @since 2.6.0 + * + * (@internal Uses `load_theme_textdomain()` rather than `load_plugin_textdomain()` to + * get round the different ways of handling the path and deprecated notices being thrown + * and such. For plugins, the actual file name will be corrected by a filter.}} + * + * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA + * generator on the website.}} + */ + public function load_textdomain() + { + if (is_textdomain_loaded('tgmpa')) { + return; + } + + if (false !== strpos(__FILE__, WP_PLUGIN_DIR) || false !== strpos(__FILE__, WPMU_PLUGIN_DIR)) { + // Plugin, we'll need to adjust the file name. + add_action('load_textdomain_mofile', array($this, 'correct_plugin_mofile'), 10, 2); + load_theme_textdomain('tgmpa', dirname(__FILE__) . '/languages'); + remove_action('load_textdomain_mofile', array($this, 'correct_plugin_mofile'), 10); + } else { + load_theme_textdomain('tgmpa', dirname(__FILE__) . '/languages'); + } + } + + /** + * Correct the .mo file name for (must-use) plugins. + * + * Themese use `/path/{locale}.mo` while plugins use `/path/{text-domain}-{locale}.mo`. + * + * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA + * generator on the website.}} + * + * @since 2.6.0 + * + * @param string $mofile Full path to the target mofile. + * @param string $domain The domain for which a language file is being loaded. + * @return string $mofile + */ + public function correct_plugin_mofile($mofile, $domain) + { + // Exit early if not our domain (just in case). + if ('tgmpa' !== $domain) { + return $mofile; + } + return preg_replace('`/([a-z]{2}_[A-Z]{2}.mo)$`', '/tgmpa-$1', $mofile); + } + + /** + * Potentially overload the fall-back translation file for the current language. + * + * WP, by default since WP 3.7, will load a local translation first and if none + * can be found, will try and find a translation in the /wp-content/languages/ directory. + * As this library is theme/plugin agnostic, translation files for TGMPA can exist both + * in the WP_LANG_DIR /plugins/ subdirectory as well as in the /themes/ subdirectory. + * + * This method makes sure both directories are checked. + * + * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA + * generator on the website.}} + * + * @since 2.6.0 + * + * @param string $mofile Full path to the target mofile. + * @param string $domain The domain for which a language file is being loaded. + * @return string $mofile + */ + public function overload_textdomain_mofile($mofile, $domain) + { + // Exit early if not our domain, not a WP_LANG_DIR load or if the file exists and is readable. + if ('tgmpa' !== $domain || false === strpos($mofile, WP_LANG_DIR) || @is_readable($mofile)) { + return $mofile; + } + + // Current fallback file is not valid, let's try the alternative option. + if (false !== strpos($mofile, '/themes/')) { + return str_replace('/themes/', '/plugins/', $mofile); + } elseif (false !== strpos($mofile, '/plugins/')) { + return str_replace('/plugins/', '/themes/', $mofile); + } else { + return $mofile; + } + } + + /** + * Hook in plugin action link filters for the WP native plugins page. + * + * - Prevent activation of plugins which don't meet the minimum version requirements. + * - Prevent deactivation of force-activated plugins. + * - Add update notice if update available. + * + * @since 2.5.0 + */ + public function add_plugin_action_link_filters() + { + foreach ($this->plugins as $slug => $plugin) { + if (false === $this->can_plugin_activate($slug)) { + add_filter('plugin_action_links_' . $plugin['file_path'], array($this, 'filter_plugin_action_links_activate'), 20); + } + + if (true === $plugin['force_activation']) { + add_filter('plugin_action_links_' . $plugin['file_path'], array($this, 'filter_plugin_action_links_deactivate'), 20); + } + + if (false !== $this->does_plugin_require_update($slug)) { + add_filter('plugin_action_links_' . $plugin['file_path'], array($this, 'filter_plugin_action_links_update'), 20); + } + } + } + + /** + * Remove the 'Activate' link on the WP native plugins page if the plugin does not meet the + * minimum version requirements. + * + * @since 2.5.0 + * + * @param array $actions Action links. + * @return array + */ + public function filter_plugin_action_links_activate($actions) + { + unset($actions['activate']); + + return $actions; + } + + /** + * Remove the 'Deactivate' link on the WP native plugins page if the plugin has been set to force activate. + * + * @since 2.5.0 + * + * @param array $actions Action links. + * @return array + */ + public function filter_plugin_action_links_deactivate($actions) + { + unset($actions['deactivate']); + + return $actions; + } + + /** + * Add a 'Requires update' link on the WP native plugins page if the plugin does not meet the + * minimum version requirements. + * + * @since 2.5.0 + * + * @param array $actions Action links. + * @return array + */ + public function filter_plugin_action_links_update($actions) + { + $actions['update'] = sprintf( + '%3$s', + esc_url($this->get_tgmpa_status_url('update')), + esc_attr__('This plugin needs to be updated to be compatible with your theme.', 'tgmpa'), + esc_html__('Update Required', 'tgmpa') + ); + + return $actions; + } + + /** + * Handles calls to show plugin information via links in the notices. + * + * We get the links in the admin notices to point to the TGMPA page, rather + * than the typical plugin-install.php file, so we can prepare everything + * beforehand. + * + * WP does not make it easy to show the plugin information in the thickbox - + * here we have to require a file that includes a function that does the + * main work of displaying it, enqueue some styles, set up some globals and + * finally call that function before exiting. + * + * Down right easy once you know how... + * + * Returns early if not the TGMPA page. + * + * @since 2.1.0 + * + * @global string $tab Used as iframe div class names, helps with styling + * @global string $body_id Used as the iframe body ID, helps with styling + * + * @return null Returns early if not the TGMPA page. + */ + public function admin_init() + { + if (!$this->is_tgmpa_page()) { + return; + } + + if (isset($_REQUEST['tab']) && 'plugin-information' === $_REQUEST['tab']) { + // Needed for install_plugin_information(). + require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; + + wp_enqueue_style('plugin-install'); + + global $tab, $body_id; + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WP requirement. + $body_id = 'plugin-information'; + + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Overriding the WP global is the point. + $tab = 'plugin-information'; + + install_plugin_information(); + + exit; + } + } + + /** + * Enqueue thickbox scripts/styles for plugin info. + * + * Thickbox is not automatically included on all admin pages, so we must + * manually enqueue it for those pages. + * + * Thickbox is only loaded if the user has not dismissed the admin + * notice or if there are any plugins left to install and activate. + * + * @since 2.1.0 + */ + public function thickbox() + { + if (!get_user_meta(get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true)) { + add_thickbox(); + } + } + + /** + * Adds submenu page if there are plugin actions to take. + * + * This method adds the submenu page letting users know that a required + * plugin needs to be installed. + * + * This page disappears once the plugin has been installed and activated. + * + * @since 1.0.0 + * + * @see TGM_Plugin_Activation::init() + * @see TGM_Plugin_Activation::install_plugins_page() + * + * @return null Return early if user lacks capability to install a plugin. + */ + public function admin_menu() + { + // Make sure privileges are correct to see the page. + if (!current_user_can('install_plugins')) { + return; + } + + $args = apply_filters( + 'tgmpa_admin_menu_args', + array( + 'parent_slug' => $this->parent_slug, // Parent Menu slug. + 'page_title' => $this->strings['page_title'], // Page title. + 'menu_title' => $this->strings['menu_title'], // Menu title. + 'capability' => $this->capability, // Capability. + 'menu_slug' => $this->menu, // Menu slug. + 'function' => array($this, 'install_plugins_page'), // Callback. + ) + ); + + $this->add_admin_menu($args); + } + + /** + * Add the menu item. + * + * {@internal IMPORTANT! If this function changes, review the regex in the custom TGMPA + * generator on the website.}} + * + * @since 2.5.0 + * + * @param array $args Menu item configuration. + */ + protected function add_admin_menu(array $args) + { + if (has_filter('tgmpa_admin_menu_use_add_theme_page')) { + _deprecated_function('The "tgmpa_admin_menu_use_add_theme_page" filter', '2.5.0', esc_html__('Set the parent_slug config variable instead.', 'tgmpa')); + } + + if ('themes.php' === $this->parent_slug) { + $this->page_hook = call_user_func('add_theme_page', $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function']); + } else { + $this->page_hook = call_user_func('add_submenu_page', $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function']); + } + } + + /** + * Echoes plugin installation form. + * + * This method is the callback for the admin_menu method function. + * This displays the admin page and form area where the user can select to install and activate the plugin. + * Aborts early if we're processing a plugin installation action. + * + * @since 1.0.0 + * + * @return null Aborts early if we're processing a plugin installation action. + */ + public function install_plugins_page() + { + // Store new instance of plugin table in object. + $plugin_table = new TGMPA_List_Table(); + + // Return early if processing a plugin installation action. + if ((('tgmpa-bulk-install' === $plugin_table->current_action() || 'tgmpa-bulk-update' === $plugin_table->current_action()) && $plugin_table->process_bulk_actions()) || $this->do_plugin_install()) { + return; + } + + // Force refresh of available plugin information so we'll know about manual updates/deletes. + wp_clean_plugins_cache(false); + +?> +
+

+ prepare_items(); ?> + message) && is_string($this->message)) { + echo wp_kses_post($this->message); + } + ?> + views(); ?> +
+ + + display(); ?> +
+
+sanitize_key(urldecode($_GET['plugin'])); + + if (!isset($this->plugins[$slug])) { + return false; + } + + // Was an install or upgrade action link clicked? + if ((isset($_GET['tgmpa-install']) && 'install-plugin' === $_GET['tgmpa-install']) || (isset($_GET['tgmpa-update']) && 'update-plugin' === $_GET['tgmpa-update'])) { + + $install_type = 'install'; + if (isset($_GET['tgmpa-update']) && 'update-plugin' === $_GET['tgmpa-update']) { + $install_type = 'update'; + } + + check_admin_referer('tgmpa-' . $install_type, 'tgmpa-nonce'); + + // Pass necessary information via URL if WP_Filesystem is needed. + $url = wp_nonce_url( + add_query_arg( + array( + 'plugin' => urlencode($slug), + 'tgmpa-' . $install_type => $install_type . '-plugin', + ), + $this->get_tgmpa_url() + ), + 'tgmpa-' . $install_type, + 'tgmpa-nonce' + ); + + $method = ''; // Leave blank so WP_Filesystem can populate it as necessary. + + $creds = request_filesystem_credentials(esc_url_raw($url), $method, false, false, array()); + if (false === $creds) { + return true; + } + + if (!WP_Filesystem($creds)) { + request_filesystem_credentials(esc_url_raw($url), $method, true, false, array()); // Setup WP_Filesystem. + return true; + } + + /* If we arrive here, we have the filesystem. */ + + // Prep variables for Plugin_Installer_Skin class. + $extra = array(); + $extra['slug'] = $slug; // Needed for potentially renaming of directory name. + $source = $this->get_download_url($slug); + $api = ('repo' === $this->plugins[$slug]['source_type']) ? $this->get_plugins_api($slug) : null; + $api = (false !== $api) ? $api : null; + + $url = add_query_arg( + array( + 'action' => $install_type . '-plugin', + 'plugin' => urlencode($slug), + ), + 'update.php' + ); + + if (!class_exists('Plugin_Upgrader', false)) { + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + } + + $title = ('update' === $install_type) ? $this->strings['updating'] : $this->strings['installing']; + $skin_args = array( + 'type' => ('bundled' !== $this->plugins[$slug]['source_type']) ? 'web' : 'upload', + 'title' => sprintf($title, $this->plugins[$slug]['name']), + 'url' => esc_url_raw($url), + 'nonce' => $install_type . '-plugin_' . $slug, + 'plugin' => '', + 'api' => $api, + 'extra' => $extra, + ); + unset($title); + + if ('update' === $install_type) { + $skin_args['plugin'] = $this->plugins[$slug]['file_path']; + $skin = new Plugin_Upgrader_Skin($skin_args); + } else { + $skin = new Plugin_Installer_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($this, 'maybe_adjust_source_dir'), 1, 3); + + if ('update' === $install_type) { + // Inject our info into the update transient. + $to_inject = array( + $slug => $this->plugins[$slug], + ); + $to_inject[$slug]['source'] = $source; + $this->inject_update_info($to_inject); + + $upgrader->upgrade($this->plugins[$slug]['file_path']); + } else { + $upgrader->install($source); + } + + remove_filter('upgrader_source_selection', array($this, 'maybe_adjust_source_dir'), 1); + + // Make sure we have the correct file path now the plugin is installed/updated. + $this->populate_file_path($slug); + + // Only activate plugins if the config option is set to true and the plugin isn't + // already active (upgrade). + if ($this->is_automatic && !$this->is_plugin_active($slug)) { + $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method. + if (false === $this->activate_single_plugin($plugin_activate, $slug, true)) { + return true; // Finish execution of the function early as we encountered an error. + } + } + + $this->show_tgmpa_version(); + + // Display message based on if all plugins are now active or not. + if ($this->is_tgmpa_complete()) { + echo '

', sprintf(esc_html($this->strings['complete']), '' . esc_html($this->strings['dashboard']) . ''), '

'; + echo ''; + } else { + echo '

', esc_html($this->strings['return']), '

'; + } + + return true; + } elseif (isset($this->plugins[$slug]['file_path'], $_GET['tgmpa-activate']) && 'activate-plugin' === $_GET['tgmpa-activate']) { + // Activate action link was clicked. + check_admin_referer('tgmpa-activate', 'tgmpa-nonce'); + + if (false === $this->activate_single_plugin($this->plugins[$slug]['file_path'], $slug)) { + return true; // Finish execution of the function early as we encountered an error. + } + } + + return false; + } + + /** + * Inject information into the 'update_plugins' site transient as WP checks that before running an update. + * + * @since 2.5.0 + * + * @param array $plugins The plugin information for the plugins which are to be updated. + */ + public function inject_update_info($plugins) + { + $repo_updates = get_site_transient('update_plugins'); + + if (!is_object($repo_updates)) { + $repo_updates = new stdClass(); + } + + foreach ($plugins as $slug => $plugin) { + $file_path = $plugin['file_path']; + + if (empty($repo_updates->response[$file_path])) { + $repo_updates->response[$file_path] = new stdClass(); + } + + // We only really need to set package, but let's do all we can in case WP changes something. + $repo_updates->response[$file_path]->slug = $slug; + $repo_updates->response[$file_path]->plugin = $file_path; + $repo_updates->response[$file_path]->new_version = $plugin['version']; + $repo_updates->response[$file_path]->package = $plugin['source']; + if (empty($repo_updates->response[$file_path]->url) && !empty($plugin['external_url'])) { + $repo_updates->response[$file_path]->url = $plugin['external_url']; + } + } + + set_site_transient('update_plugins', $repo_updates); + } + + /** + * Adjust the plugin directory name if necessary. + * + * The final destination directory of a plugin is based on the subdirectory name found in the + * (un)zipped source. In some cases - most notably GitHub repository plugin downloads -, 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. + * + * @since 2.5.0 + * + * @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 $source + */ + public function maybe_adjust_source_dir($source, $remote_source, $upgrader) + { + if (!$this->is_tgmpa_page() || !is_object($GLOBALS['wp_filesystem'])) { + return $source; + } + + // Check for single file plugins. + $source_files = array_keys($GLOBALS['wp_filesystem']->dirlist($remote_source)); + if (1 === count($source_files) && false === $GLOBALS['wp_filesystem']->is_dir($source)) { + return $source; + } + + // Multi-file plugin, let's see if the directory is correctly named. + $desired_slug = ''; + + // Figure out what the slug is supposed to be. + if (false === $upgrader->bulk && !empty($upgrader->skin->options['extra']['slug'])) { + $desired_slug = $upgrader->skin->options['extra']['slug']; + } else { + // Bulk installer contains less info, so fall back on the info registered here. + foreach ($this->plugins as $slug => $plugin) { + if (!empty($upgrader->skin->plugin_names[$upgrader->skin->i]) && $plugin['name'] === $upgrader->skin->plugin_names[$upgrader->skin->i]) { + $desired_slug = $slug; + break; + } + } + unset($slug, $plugin); + } + + if (!empty($desired_slug)) { + $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); + } else { + return new WP_Error( + 'rename_failed', + esc_html__('The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'tgmpa') . ' ' . esc_html__('Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa'), + array( + 'found' => $subdir_name, + 'expected' => $desired_slug, + ) + ); + } + } elseif (empty($subdir_name)) { + return new WP_Error( + 'packaged_wrong', + esc_html__('The remote plugin package consists of more than one file, but the files are not packaged in a folder.', 'tgmpa') . ' ' . esc_html__('Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa'), + array( + 'found' => $subdir_name, + 'expected' => $desired_slug, + ) + ); + } + } + + return $source; + } + + /** + * Activate a single plugin and send feedback about the result to the screen. + * + * @since 2.5.0 + * + * @param string $file_path Path within wp-plugins/ to main plugin file. + * @param string $slug Plugin slug. + * @param bool $automatic Whether this is an automatic activation after an install. Defaults to false. + * This determines the styling of the output messages. + * @return bool False if an error was encountered, true otherwise. + */ + protected function activate_single_plugin($file_path, $slug, $automatic = false) + { + if ($this->can_plugin_activate($slug)) { + $activate = activate_plugin($file_path); + + if (is_wp_error($activate)) { + echo '

', wp_kses_post($activate->get_error_message()), '

', + '

', esc_html($this->strings['return']), '

'; + + return false; // End it here if there is an error with activation. + } else { + if (!$automatic) { + // Make sure message doesn't display again if bulk activation is performed + // immediately after a single activation. + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Not using the superglobal. + if (!isset($_POST['action'])) { + echo '

', esc_html($this->strings['activated_successfully']), ' ', esc_html($this->plugins[$slug]['name']), '.

'; + } + } else { + // Simpler message layout for use on the plugin install page. + echo '

', esc_html($this->strings['plugin_activated']), '

'; + } + } + } elseif ($this->is_plugin_active($slug)) { + // No simpler message format provided as this message should never be encountered + // on the plugin install page. + echo '

', + sprintf( + esc_html($this->strings['plugin_already_active']), + '' . esc_html($this->plugins[$slug]['name']) . '' + ), + '

'; + } elseif ($this->does_plugin_require_update($slug)) { + if (!$automatic) { + // Make sure message doesn't display again if bulk activation is performed + // immediately after a single activation. + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Not using the superglobal. + if (!isset($_POST['action'])) { + echo '

', + sprintf( + esc_html($this->strings['plugin_needs_higher_version']), + '' . esc_html($this->plugins[$slug]['name']) . '' + ), + '

'; + } + } else { + // Simpler message layout for use on the plugin install page. + echo '

', sprintf(esc_html($this->strings['plugin_needs_higher_version']), esc_html($this->plugins[$slug]['name'])), '

'; + } + } + + return true; + } + + /** + * Echoes required plugin notice. + * + * Outputs a message telling users that a specific plugin is required for + * their theme. If appropriate, it includes a link to the form page where + * users can install and activate the plugin. + * + * Returns early if we're on the Install page. + * + * @since 1.0.0 + * + * @global object $current_screen + * + * @return null Returns early if we're on the Install page. + */ + public function notices() + { + // Remove nag on the install page / Return early if the nag message has been dismissed or user < author. + if (($this->is_tgmpa_page() || $this->is_core_update_page()) || get_user_meta(get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true) || !current_user_can(apply_filters('tgmpa_show_admin_notice_capability', 'publish_posts'))) { + return; + } + + // Store for the plugin slugs by message type. + $message = array(); + + // Initialize counters used to determine plurality of action link texts. + $install_link_count = 0; + $update_link_count = 0; + $activate_link_count = 0; + $total_required_action_count = 0; + + foreach ($this->plugins as $slug => $plugin) { + if ($this->is_plugin_active($slug) && false === $this->does_plugin_have_update($slug)) { + continue; + } + + if (!$this->is_plugin_installed($slug)) { + if (current_user_can('install_plugins')) { + $install_link_count++; + + if (true === $plugin['required']) { + $message['notice_can_install_required'][] = $slug; + } else { + $message['notice_can_install_recommended'][] = $slug; + } + } + if (true === $plugin['required']) { + $total_required_action_count++; + } + } else { + if (!$this->is_plugin_active($slug) && $this->can_plugin_activate($slug)) { + if (current_user_can('activate_plugins')) { + $activate_link_count++; + + if (true === $plugin['required']) { + $message['notice_can_activate_required'][] = $slug; + } else { + $message['notice_can_activate_recommended'][] = $slug; + } + } + if (true === $plugin['required']) { + $total_required_action_count++; + } + } + + if ($this->does_plugin_require_update($slug) || false !== $this->does_plugin_have_update($slug)) { + + if (current_user_can('update_plugins')) { + $update_link_count++; + + if ($this->does_plugin_require_update($slug)) { + $message['notice_ask_to_update'][] = $slug; + } elseif (false !== $this->does_plugin_have_update($slug)) { + $message['notice_ask_to_update_maybe'][] = $slug; + } + } + if (true === $plugin['required']) { + $total_required_action_count++; + } + } + } + } + unset($slug, $plugin); + + // If we have notices to display, we move forward. + if (!empty($message) || $total_required_action_count > 0) { + krsort($message); // Sort messages. + $rendered = ''; + + // As add_settings_error() wraps the final message in a

and as the final message can't be + // filtered, using

's in our html would render invalid html output. + $line_template = '%s' . "\n"; + + if (!current_user_can('activate_plugins') && !current_user_can('install_plugins') && !current_user_can('update_plugins')) { + $rendered = esc_html($this->strings['notice_cannot_install_activate']) . ' ' . esc_html($this->strings['contact_admin']); + $rendered .= $this->create_user_action_links_for_notice(0, 0, 0, $line_template); + } else { + + // If dismissable is false and a message is set, output it now. + if (!$this->dismissable && !empty($this->dismiss_msg)) { + $rendered .= sprintf($line_template, wp_kses_post($this->dismiss_msg)); + } + + // Render the individual message lines for the notice. + foreach ($message as $type => $plugin_group) { + $linked_plugins = array(); + + // Get the external info link for a plugin if one is available. + foreach ($plugin_group as $plugin_slug) { + $linked_plugins[] = $this->get_info_link($plugin_slug); + } + unset($plugin_slug); + + $count = count($plugin_group); + $linked_plugins = array_map(array('TGMPA_Utils', 'wrap_in_em'), $linked_plugins); + $last_plugin = array_pop($linked_plugins); // Pop off last name to prep for readability. + $imploded = empty($linked_plugins) ? $last_plugin : (implode(', ', $linked_plugins) . ' ' . esc_html_x('and', 'plugin A *and* plugin B', 'tgmpa') . ' ' . $last_plugin); + + $rendered .= sprintf( + $line_template, + sprintf( + translate_nooped_plural($this->strings[$type], $count, 'tgmpa'), + $imploded, + $count + ) + ); + } + unset($type, $plugin_group, $linked_plugins, $count, $last_plugin, $imploded); + + $rendered .= $this->create_user_action_links_for_notice($install_link_count, $update_link_count, $activate_link_count, $line_template); + } + + // Register the nag messages and prepare them to be processed. + add_settings_error('tgmpa', 'tgmpa', $rendered, $this->get_admin_notice_class()); + } + + // Admin options pages already output settings_errors, so this is to avoid duplication. + if ('options-general' !== $GLOBALS['current_screen']->parent_base) { + $this->display_settings_errors(); + } + } + + /** + * Generate the user action links for the admin notice. + * + * @since 2.6.0 + * + * @param int $install_count Number of plugins to install. + * @param int $update_count Number of plugins to update. + * @param int $activate_count Number of plugins to activate. + * @param int $line_template Template for the HTML tag to output a line. + * @return string Action links. + */ + protected function create_user_action_links_for_notice($install_count, $update_count, $activate_count, $line_template) + { + // Setup action links. + $action_links = array( + 'install' => '', + 'update' => '', + 'activate' => '', + 'dismiss' => $this->dismissable ? '' . esc_html($this->strings['dismiss']) . '' : '', + ); + + $link_template = '%1$s'; + + if (current_user_can('install_plugins')) { + if ($install_count > 0) { + $action_links['install'] = sprintf( + $link_template, + translate_nooped_plural($this->strings['install_link'], $install_count, 'tgmpa'), + esc_url($this->get_tgmpa_status_url('install')) + ); + } + if ($update_count > 0) { + $action_links['update'] = sprintf( + $link_template, + translate_nooped_plural($this->strings['update_link'], $update_count, 'tgmpa'), + esc_url($this->get_tgmpa_status_url('update')) + ); + } + } + + if (current_user_can('activate_plugins') && $activate_count > 0) { + $action_links['activate'] = sprintf( + $link_template, + translate_nooped_plural($this->strings['activate_link'], $activate_count, 'tgmpa'), + esc_url($this->get_tgmpa_status_url('activate')) + ); + } + + $action_links = apply_filters('tgmpa_notice_action_links', $action_links); + + $action_links = array_filter((array) $action_links); // Remove any empty array items. + + if (!empty($action_links)) { + $action_links = sprintf($line_template, implode(' | ', $action_links)); + return apply_filters('tgmpa_notice_rendered_action_links', $action_links); + } else { + return ''; + } + } + + /** + * Get admin notice class. + * + * Work around all the changes to the various admin notice classes between WP 4.4 and 3.7 + * (lowest supported version by TGMPA). + * + * @since 2.6.0 + * + * @return string + */ + protected function get_admin_notice_class() + { + if (!empty($this->strings['nag_type'])) { + return sanitize_html_class(strtolower($this->strings['nag_type'])); + } else { + if (version_compare($this->wp_version, '4.2', '>=')) { + return 'notice-warning'; + } elseif (version_compare($this->wp_version, '4.1', '>=')) { + return 'notice'; + } else { + return 'updated'; + } + } + } + + /** + * Display settings errors and remove those which have been displayed to avoid duplicate messages showing + * + * @since 2.5.0 + */ + protected function display_settings_errors() + { + global $wp_settings_errors; + + settings_errors('tgmpa'); + + foreach ((array) $wp_settings_errors as $key => $details) { + if ('tgmpa' === $details['setting']) { + unset($wp_settings_errors[$key]); + break; + } + } + } + + /** + * Register dismissal of admin notices. + * + * Acts on the dismiss link in the admin nag messages. + * If clicked, the admin notice disappears and will no longer be visible to this user. + * + * @since 2.1.0 + */ + public function dismiss() + { + if (isset($_GET['tgmpa-dismiss']) && check_admin_referer('tgmpa-dismiss-' . get_current_user_id())) { + update_user_meta(get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, 1); + } + } + + /** + * Add individual plugin to our collection of plugins. + * + * If the required keys are not set or the plugin has already + * been registered, the plugin is not added. + * + * @since 2.0.0 + * + * @param array|null $plugin Array of plugin arguments or null if invalid argument. + * @return null Return early if incorrect argument. + */ + public function register($plugin) + { + if (empty($plugin['slug']) || empty($plugin['name'])) { + return; + } + + if (empty($plugin['slug']) || !is_string($plugin['slug']) || isset($this->plugins[$plugin['slug']])) { + return; + } + + $defaults = array( + 'name' => '', // String. + 'slug' => '', // String. + 'source' => 'repo', // String. + 'required' => false, // Boolean. + 'version' => '', // String. + 'force_activation' => false, // Boolean. + 'force_deactivation' => false, // Boolean. + 'external_url' => '', // String. + 'is_callable' => '', // String or array. + ); + + // Prepare the received data. + $plugin = wp_parse_args($plugin, $defaults); + + // Standardize the received slug. + $plugin['slug'] = $this->sanitize_key($plugin['slug']); + + // Forgive users for using string versions of booleans or floats for version number. + $plugin['version'] = (string) $plugin['version']; + $plugin['source'] = empty($plugin['source']) ? 'repo' : $plugin['source']; + $plugin['required'] = TGMPA_Utils::validate_bool($plugin['required']); + $plugin['force_activation'] = TGMPA_Utils::validate_bool($plugin['force_activation']); + $plugin['force_deactivation'] = TGMPA_Utils::validate_bool($plugin['force_deactivation']); + + // Enrich the received data. + $plugin['file_path'] = $this->_get_plugin_basename_from_slug($plugin['slug']); + $plugin['source_type'] = $this->get_plugin_source_type($plugin['source']); + + // Set the class properties. + $this->plugins[$plugin['slug']] = $plugin; + $this->sort_order[$plugin['slug']] = $plugin['name']; + + // Should we add the force activation hook ? + if (true === $plugin['force_activation']) { + $this->has_forced_activation = true; + } + + // Should we add the force deactivation hook ? + if (true === $plugin['force_deactivation']) { + $this->has_forced_deactivation = true; + } + } + + /** + * Determine what type of source the plugin comes from. + * + * @since 2.5.0 + * + * @param string $source The source of the plugin as provided, either empty (= WP repo), a file path + * (= bundled) or an external URL. + * @return string 'repo', 'external', or 'bundled' + */ + protected function get_plugin_source_type($source) + { + if ('repo' === $source || preg_match(self::WP_REPO_REGEX, $source)) { + return 'repo'; + } elseif (preg_match(self::IS_URL_REGEX, $source)) { + return 'external'; + } else { + return 'bundled'; + } + } + + /** + * Sanitizes a string key. + * + * Near duplicate of WP Core `sanitize_key()`. The difference is that uppercase characters *are* + * allowed, so as not to break upgrade paths from non-standard bundled plugins using uppercase + * characters in the plugin directory path/slug. Silly them. + * + * @see https://developer.wordpress.org/reference/hooks/sanitize_key/ + * + * @since 2.5.0 + * + * @param string $key String key. + * @return string Sanitized key + */ + public function sanitize_key($key) + { + $raw_key = $key; + $key = preg_replace('`[^A-Za-z0-9_-]`', '', $key); + + /** + * Filter a sanitized key string. + * + * @since 2.5.0 + * + * @param string $key Sanitized key. + * @param string $raw_key The key prior to sanitization. + */ + return apply_filters('tgmpa_sanitize_key', $key, $raw_key); + } + + /** + * Amend default configuration settings. + * + * @since 2.0.0 + * + * @param array $config Array of config options to pass as class properties. + */ + public function config($config) + { + $keys = array( + 'id', + 'default_path', + 'has_notices', + 'dismissable', + 'dismiss_msg', + 'menu', + 'parent_slug', + 'capability', + 'is_automatic', + 'message', + 'strings', + ); + + foreach ($keys as $key) { + if (isset($config[$key])) { + if (is_array($config[$key])) { + $this->$key = array_merge($this->$key, $config[$key]); + } else { + $this->$key = $config[$key]; + } + } + } + } + + /** + * Amend action link after plugin installation. + * + * @since 2.0.0 + * + * @param array $install_actions Existing array of actions. + * @return false|array Amended array of actions. + */ + public function actions($install_actions) + { + // Remove action links on the TGMPA install page. + if ($this->is_tgmpa_page()) { + return false; + } + + return $install_actions; + } + + /** + * Flushes the plugins cache on theme switch to prevent stale entries + * from remaining in the plugin table. + * + * @since 2.4.0 + * + * @param bool $clear_update_cache Optional. Whether to clear the Plugin updates cache. + * Parameter added in v2.5.0. + */ + public function flush_plugins_cache($clear_update_cache = true) + { + wp_clean_plugins_cache($clear_update_cache); + } + + /** + * Set file_path key for each installed plugin. + * + * @since 2.1.0 + * + * @param string $plugin_slug Optional. If set, only (re-)populates the file path for that specific plugin. + * Parameter added in v2.5.0. + */ + public function populate_file_path($plugin_slug = '') + { + if (!empty($plugin_slug) && is_string($plugin_slug) && isset($this->plugins[$plugin_slug])) { + $this->plugins[$plugin_slug]['file_path'] = $this->_get_plugin_basename_from_slug($plugin_slug); + } else { + // Add file_path key for all plugins. + foreach ($this->plugins as $slug => $values) { + $this->plugins[$slug]['file_path'] = $this->_get_plugin_basename_from_slug($slug); + } + } + } + + /** + * Helper function to extract the file path of the plugin file from the + * plugin slug, if the plugin is installed. + * + * @since 2.0.0 + * + * @param string $slug Plugin slug (typically folder name) as provided by the developer. + * @return string Either file path for plugin if installed, or just the plugin slug. + */ + protected function _get_plugin_basename_from_slug($slug) + { + $keys = array_keys($this->get_plugins()); + + foreach ($keys as $key) { + if (preg_match('|^' . $slug . '/|', $key)) { + return $key; + } + } + + return $slug; + } + + /** + * Retrieve plugin data, given the plugin name. + * + * Loops through the registered plugins looking for $name. If it finds it, + * it returns the $data from that plugin. Otherwise, returns false. + * + * @since 2.1.0 + * + * @param string $name Name of the plugin, as it was registered. + * @param string $data Optional. Array key of plugin data to return. Default is slug. + * @return string|boolean Plugin slug if found, false otherwise. + */ + public function _get_plugin_data_from_name($name, $data = 'slug') + { + foreach ($this->plugins as $values) { + if ($name === $values['name'] && isset($values[$data])) { + return $values[$data]; + } + } + + return false; + } + + /** + * Retrieve the download URL for a package. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return string Plugin download URL or path to local file or empty string if undetermined. + */ + public function get_download_url($slug) + { + $dl_source = ''; + + switch ($this->plugins[$slug]['source_type']) { + case 'repo': + return $this->get_wp_repo_download_url($slug); + case 'external': + return $this->plugins[$slug]['source']; + case 'bundled': + return $this->default_path . $this->plugins[$slug]['source']; + } + + return $dl_source; // Should never happen. + } + + /** + * Retrieve the download URL for a WP repo package. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return string Plugin download URL. + */ + protected function get_wp_repo_download_url($slug) + { + $source = ''; + $api = $this->get_plugins_api($slug); + + if (false !== $api && isset($api->download_link)) { + $source = $api->download_link; + } + + return $source; + } + + /** + * Try to grab information from WordPress API. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return object Plugins_api response object on success, WP_Error on failure. + */ + protected function get_plugins_api($slug) + { + static $api = array(); // Cache received responses. + + if (!isset($api[$slug])) { + if (!function_exists('plugins_api')) { + require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; + } + + $response = plugins_api( + 'plugin_information', + array( + 'slug' => $slug, + 'fields' => array( + 'sections' => false, + ), + ) + ); + + $api[$slug] = false; + + if (is_wp_error($response)) { + wp_die(esc_html($this->strings['oops'])); + } else { + $api[$slug] = $response; + } + } + + return $api[$slug]; + } + + /** + * Retrieve a link to a plugin information page. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return string Fully formed html link to a plugin information page if available + * or the plugin name if not. + */ + public function get_info_link($slug) + { + if (!empty($this->plugins[$slug]['external_url']) && preg_match(self::IS_URL_REGEX, $this->plugins[$slug]['external_url'])) { + $link = sprintf( + '%2$s', + esc_url($this->plugins[$slug]['external_url']), + esc_html($this->plugins[$slug]['name']) + ); + } elseif ('repo' === $this->plugins[$slug]['source_type']) { + $url = add_query_arg( + array( + 'tab' => 'plugin-information', + 'plugin' => urlencode($slug), + 'TB_iframe' => 'true', + 'width' => '640', + 'height' => '500', + ), + self_admin_url('plugin-install.php') + ); + + $link = sprintf( + '%2$s', + esc_url($url), + esc_html($this->plugins[$slug]['name']) + ); + } else { + $link = esc_html($this->plugins[$slug]['name']); // No hyperlink. + } + + return $link; + } + + /** + * Determine if we're on the TGMPA Install page. + * + * @since 2.1.0 + * + * @return boolean True when on the TGMPA page, false otherwise. + */ + protected function is_tgmpa_page() + { + return isset($_GET['page']) && $this->menu === $_GET['page']; + } + + /** + * Determine if we're on a WP Core installation/upgrade page. + * + * @since 2.6.0 + * + * @return boolean True when on a WP Core installation/upgrade page, false otherwise. + */ + protected function is_core_update_page() + { + // Current screen is not always available, most notably on the customizer screen. + if (!function_exists('get_current_screen')) { + return false; + } + + $screen = get_current_screen(); + + if ('update-core' === $screen->base) { + // Core update screen. + return true; + } elseif ('plugins' === $screen->base && !empty($_POST['action'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + // Plugins bulk update screen. + return true; + } elseif ('update' === $screen->base && !empty($_POST['action'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + // Individual updates (ajax call). + return true; + } + + return false; + } + + /** + * Retrieve the URL to the TGMPA Install page. + * + * I.e. depending on the config settings passed something along the lines of: + * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins + * + * @since 2.5.0 + * + * @return string Properly encoded URL (not escaped). + */ + public function get_tgmpa_url() + { + static $url; + + if (!isset($url)) { + $parent = $this->parent_slug; + if (false === strpos($parent, '.php')) { + $parent = 'admin.php'; + } + $url = add_query_arg( + array( + 'page' => urlencode($this->menu), + ), + self_admin_url($parent) + ); + } + + return $url; + } + + /** + * Retrieve the URL to the TGMPA Install page for a specific plugin status (view). + * + * I.e. depending on the config settings passed something along the lines of: + * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins&plugin_status=install + * + * @since 2.5.0 + * + * @param string $status Plugin status - either 'install', 'update' or 'activate'. + * @return string Properly encoded URL (not escaped). + */ + public function get_tgmpa_status_url($status) + { + return add_query_arg( + array( + 'plugin_status' => urlencode($status), + ), + $this->get_tgmpa_url() + ); + } + + /** + * Determine whether there are open actions for plugins registered with TGMPA. + * + * @since 2.5.0 + * + * @return bool True if complete, i.e. no outstanding actions. False otherwise. + */ + public function is_tgmpa_complete() + { + $complete = true; + foreach ($this->plugins as $slug => $plugin) { + if (!$this->is_plugin_active($slug) || false !== $this->does_plugin_have_update($slug)) { + $complete = false; + break; + } + } + + return $complete; + } + + /** + * Check if a plugin is installed. Does not take must-use plugins into account. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return bool True if installed, false otherwise. + */ + public function is_plugin_installed($slug) + { + $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached). + + return (!empty($installed_plugins[$this->plugins[$slug]['file_path']])); + } + + /** + * Check if a plugin is active. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return bool True if active, false otherwise. + */ + public function is_plugin_active($slug) + { + return ((!empty($this->plugins[$slug]['is_callable']) && is_callable($this->plugins[$slug]['is_callable'])) || is_plugin_active($this->plugins[$slug]['file_path'])); + } + + /** + * Check if a plugin can be updated, i.e. if we have information on the minimum WP version required + * available, check whether the current install meets them. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return bool True if OK to update, false otherwise. + */ + public function can_plugin_update($slug) + { + // We currently can't get reliable info on non-WP-repo plugins - issue #380. + if ('repo' !== $this->plugins[$slug]['source_type']) { + return true; + } + + $api = $this->get_plugins_api($slug); + + if (false !== $api && isset($api->requires)) { + return version_compare($this->wp_version, $api->requires, '>='); + } + + // No usable info received from the plugins API, presume we can update. + return true; + } + + /** + * Check to see if the plugin is 'updatetable', i.e. installed, with an update available + * and no WP version requirements blocking it. + * + * @since 2.6.0 + * + * @param string $slug Plugin slug. + * @return bool True if OK to proceed with update, false otherwise. + */ + public function is_plugin_updatetable($slug) + { + if (!$this->is_plugin_installed($slug)) { + return false; + } else { + return (false !== $this->does_plugin_have_update($slug) && $this->can_plugin_update($slug)); + } + } + + /** + * Check if a plugin can be activated, i.e. is not currently active and meets the minimum + * plugin version requirements set in TGMPA (if any). + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return bool True if OK to activate, false otherwise. + */ + public function can_plugin_activate($slug) + { + return (!$this->is_plugin_active($slug) && !$this->does_plugin_require_update($slug)); + } + + /** + * Retrieve the version number of an installed plugin. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return string Version number as string or an empty string if the plugin is not installed + * or version unknown (plugins which don't comply with the plugin header standard). + */ + public function get_installed_version($slug) + { + $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached). + + if (!empty($installed_plugins[$this->plugins[$slug]['file_path']]['Version'])) { + return $installed_plugins[$this->plugins[$slug]['file_path']]['Version']; + } + + return ''; + } + + /** + * Check whether a plugin complies with the minimum version requirements. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return bool True when a plugin needs to be updated, otherwise false. + */ + public function does_plugin_require_update($slug) + { + $installed_version = $this->get_installed_version($slug); + $minimum_version = $this->plugins[$slug]['version']; + + return version_compare($minimum_version, $installed_version, '>'); + } + + /** + * Check whether there is an update available for a plugin. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return false|string Version number string of the available update or false if no update available. + */ + public function does_plugin_have_update($slug) + { + // Presume bundled and external plugins will point to a package which meets the minimum required version. + if ('repo' !== $this->plugins[$slug]['source_type']) { + if ($this->does_plugin_require_update($slug)) { + return $this->plugins[$slug]['version']; + } + + return false; + } + + $repo_updates = get_site_transient('update_plugins'); + + if (isset($repo_updates->response[$this->plugins[$slug]['file_path']]->new_version)) { + return $repo_updates->response[$this->plugins[$slug]['file_path']]->new_version; + } + + return false; + } + + /** + * Retrieve potential upgrade notice for a plugin. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return string The upgrade notice or an empty string if no message was available or provided. + */ + public function get_upgrade_notice($slug) + { + // We currently can't get reliable info on non-WP-repo plugins - issue #380. + if ('repo' !== $this->plugins[$slug]['source_type']) { + return ''; + } + + $repo_updates = get_site_transient('update_plugins'); + + if (!empty($repo_updates->response[$this->plugins[$slug]['file_path']]->upgrade_notice)) { + return $repo_updates->response[$this->plugins[$slug]['file_path']]->upgrade_notice; + } + + return ''; + } + + /** + * Wrapper around the core WP get_plugins function, making sure it's actually available. + * + * @since 2.5.0 + * + * @param string $plugin_folder Optional. Relative path to single plugin folder. + * @return array Array of installed plugins with plugin information. + */ + public function get_plugins($plugin_folder = '') + { + if (!function_exists('get_plugins')) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + + return get_plugins($plugin_folder); + } + + /** + * Delete dismissable nag option when theme is switched. + * + * This ensures that the user(s) is/are again reminded via nag of required + * and/or recommended plugins if they re-activate the theme. + * + * @since 2.1.1 + */ + public function update_dismiss() + { + delete_metadata('user', null, 'tgmpa_dismissed_notice_' . $this->id, null, true); + } + + /** + * Forces plugin activation if the parameter 'force_activation' is + * set to true. + * + * This allows theme authors to specify certain plugins that must be + * active at all times while using the current theme. + * + * Please take special care when using this parameter as it has the + * potential to be harmful if not used correctly. Setting this parameter + * to true will not allow the specified plugin to be deactivated unless + * the user switches themes. + * + * @since 2.2.0 + */ + public function force_activation() + { + foreach ($this->plugins as $slug => $plugin) { + if (true === $plugin['force_activation']) { + if (!$this->is_plugin_installed($slug)) { + // Oops, plugin isn't there so iterate to next condition. + continue; + } elseif ($this->can_plugin_activate($slug)) { + // There we go, activate the plugin. + activate_plugin($plugin['file_path']); + } + } + } + } + + /** + * Forces plugin deactivation if the parameter 'force_deactivation' + * is set to true and adds the plugin to the 'recently active' plugins list. + * + * This allows theme authors to specify certain plugins that must be + * deactivated upon switching from the current theme to another. + * + * Please take special care when using this parameter as it has the + * potential to be harmful if not used correctly. + * + * @since 2.2.0 + */ + public function force_deactivation() + { + $deactivated = array(); + + foreach ($this->plugins as $slug => $plugin) { + /* + * Only proceed forward if the parameter is set to true and plugin is active + * as a 'normal' (not must-use) plugin. + */ + if (true === $plugin['force_deactivation'] && is_plugin_active($plugin['file_path'])) { + deactivate_plugins($plugin['file_path']); + $deactivated[$plugin['file_path']] = time(); + } + } + + if (!empty($deactivated)) { + update_option('recently_activated', $deactivated + (array) get_option('recently_activated')); + } + } + + /** + * Echo the current TGMPA version number to the page. + * + * @since 2.5.0 + */ + public function show_tgmpa_version() + { + echo '

', + esc_html( + sprintf( + /* translators: %s: version number */ + __('TGMPA v%s', 'tgmpa'), + self::TGMPA_VERSION + ) + ), + '

'; + } + + /** + * Adds CSS to admin head. + * + * @since 2.6.2 + */ + public function admin_css() + { + if (!$this->is_tgmpa_page()) { + return; + } + + echo ' + '; + } + + /** + * Returns the singleton instance of the class. + * + * @since 2.4.0 + * + * @return \TGM_Plugin_Activation The TGM_Plugin_Activation object. + */ + public static function get_instance() + { + if (!isset(self::$instance) && !(self::$instance instanceof self)) { + self::$instance = new self(); + } + + return self::$instance; + } + } + + if (!function_exists('load_tgm_plugin_activation')) { + /** + * Ensure only one instance of the class is ever invoked. + * + * @since 2.5.0 + */ + function load_tgm_plugin_activation() + { + $GLOBALS['tgmpa'] = TGM_Plugin_Activation::get_instance(); + } + } + + if (did_action('plugins_loaded')) { + load_tgm_plugin_activation(); + } else { + add_action('plugins_loaded', 'load_tgm_plugin_activation'); + } +} + +if (!function_exists('tgmpa')) { + /** + * Helper function to register a collection of required plugins. + * + * @since 2.0.0 + * @api + * + * @param array $plugins An array of plugin arrays. + * @param array $config Optional. An array of configuration values. + */ + function tgmpa($plugins, $config = array()) + { + $instance = call_user_func(array(get_class($GLOBALS['tgmpa']), 'get_instance')); + + foreach ($plugins as $plugin) { + call_user_func(array($instance, 'register'), $plugin); + } + + if (!empty($config) && is_array($config)) { + // Send out notices for deprecated arguments passed. + if (isset($config['notices'])) { + _deprecated_argument(__FUNCTION__, '2.2.0', 'The `notices` config parameter was renamed to `has_notices` in TGMPA 2.2.0. Please adjust your configuration.'); + if (!isset($config['has_notices'])) { + $config['has_notices'] = $config['notices']; + } + } + + if (isset($config['parent_menu_slug'])) { + _deprecated_argument(__FUNCTION__, '2.4.0', 'The `parent_menu_slug` config parameter was removed in TGMPA 2.4.0. In TGMPA 2.5.0 an alternative was (re-)introduced. Please adjust your configuration. For more information visit the website: http://tgmpluginactivation.com/configuration/#h-configuration-options.'); + } + if (isset($config['parent_url_slug'])) { + _deprecated_argument(__FUNCTION__, '2.4.0', 'The `parent_url_slug` config parameter was removed in TGMPA 2.4.0. In TGMPA 2.5.0 an alternative was (re-)introduced. Please adjust your configuration. For more information visit the website: http://tgmpluginactivation.com/configuration/#h-configuration-options.'); + } + + call_user_func(array($instance, 'config'), $config); + } + } +} + +/** + * WP_List_Table isn't always available. If it isn't available, + * we load it here. + * + * @since 2.2.0 + */ +if (!class_exists('WP_List_Table')) { + require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; +} + +if (!class_exists('TGMPA_List_Table')) { + + /** + * List table class for handling plugins. + * + * Extends the WP_List_Table class to provide a future-compatible + * way of listing out all required/recommended plugins. + * + * Gives users an interface similar to the Plugin Administration + * area with similar (albeit stripped down) capabilities. + * + * This class also allows for the bulk install of plugins. + * + * @since 2.2.0 + * + * @package TGM-Plugin-Activation + * @author Thomas Griffin + * @author Gary Jones + */ + class TGMPA_List_Table extends WP_List_Table + { + /** + * TGMPA instance. + * + * @since 2.5.0 + * + * @var object + */ + protected $tgmpa; + + /** + * The currently chosen view. + * + * @since 2.5.0 + * + * @var string One of: 'all', 'install', 'update', 'activate' + */ + public $view_context = 'all'; + + /** + * The plugin counts for the various views. + * + * @since 2.5.0 + * + * @var array + */ + protected $view_totals = array( + 'all' => 0, + 'install' => 0, + 'update' => 0, + 'activate' => 0, + ); + + /** + * References parent constructor and sets defaults for class. + * + * @since 2.2.0 + */ + public function __construct() + { + $this->tgmpa = call_user_func(array(get_class($GLOBALS['tgmpa']), 'get_instance')); + + parent::__construct( + array( + 'singular' => 'plugin', + 'plural' => 'plugins', + 'ajax' => false, + ) + ); + + if (isset($_REQUEST['plugin_status']) && in_array($_REQUEST['plugin_status'], array('install', 'update', 'activate'), true)) { + $this->view_context = sanitize_key($_REQUEST['plugin_status']); + } + + add_filter('tgmpa_table_data_items', array($this, 'sort_table_items')); + } + + /** + * Get a list of CSS classes for the tag. + * + * Overruled to prevent the 'plural' argument from being added. + * + * @since 2.5.0 + * + * @return array CSS classnames. + */ + public function get_table_classes() + { + return array('widefat', 'fixed'); + } + + /** + * Gathers and renames all of our plugin information to be used by WP_List_Table to create our table. + * + * @since 2.2.0 + * + * @return array $table_data Information for use in table. + */ + protected function _gather_plugin_data() + { + // Load thickbox for plugin links. + $this->tgmpa->admin_init(); + $this->tgmpa->thickbox(); + + // Categorize the plugins which have open actions. + $plugins = $this->categorize_plugins_to_views(); + + // Set the counts for the view links. + $this->set_view_totals($plugins); + + // Prep variables for use and grab list of all installed plugins. + $table_data = array(); + $i = 0; + + // Redirect to the 'all' view if no plugins were found for the selected view context. + if (empty($plugins[$this->view_context])) { + $this->view_context = 'all'; + } + + foreach ($plugins[$this->view_context] as $slug => $plugin) { + $table_data[$i]['sanitized_plugin'] = $plugin['name']; + $table_data[$i]['slug'] = $slug; + $table_data[$i]['plugin'] = '' . $this->tgmpa->get_info_link($slug) . ''; + $table_data[$i]['source'] = $this->get_plugin_source_type_text($plugin['source_type']); + $table_data[$i]['type'] = $this->get_plugin_advise_type_text($plugin['required']); + $table_data[$i]['status'] = $this->get_plugin_status_text($slug); + $table_data[$i]['installed_version'] = $this->tgmpa->get_installed_version($slug); + $table_data[$i]['minimum_version'] = $plugin['version']; + $table_data[$i]['available_version'] = $this->tgmpa->does_plugin_have_update($slug); + + // Prep the upgrade notice info. + $upgrade_notice = $this->tgmpa->get_upgrade_notice($slug); + if (!empty($upgrade_notice)) { + $table_data[$i]['upgrade_notice'] = $upgrade_notice; + + add_action("tgmpa_after_plugin_row_{$slug}", array($this, 'wp_plugin_update_row'), 10, 2); + } + + $table_data[$i] = apply_filters('tgmpa_table_data_item', $table_data[$i], $plugin); + + $i++; + } + + return $table_data; + } + + /** + * Categorize the plugins which have open actions into views for the TGMPA page. + * + * @since 2.5.0 + */ + protected function categorize_plugins_to_views() + { + $plugins = array( + 'all' => array(), // Meaning: all plugins which still have open actions. + 'install' => array(), + 'update' => array(), + 'activate' => array(), + ); + + foreach ($this->tgmpa->plugins as $slug => $plugin) { + if ($this->tgmpa->is_plugin_active($slug) && false === $this->tgmpa->does_plugin_have_update($slug)) { + // No need to display plugins if they are installed, up-to-date and active. + continue; + } else { + $plugins['all'][$slug] = $plugin; + + if (!$this->tgmpa->is_plugin_installed($slug)) { + $plugins['install'][$slug] = $plugin; + } else { + if (false !== $this->tgmpa->does_plugin_have_update($slug)) { + $plugins['update'][$slug] = $plugin; + } + + if ($this->tgmpa->can_plugin_activate($slug)) { + $plugins['activate'][$slug] = $plugin; + } + } + } + } + + return $plugins; + } + + /** + * Set the counts for the view links. + * + * @since 2.5.0 + * + * @param array $plugins Plugins order by view. + */ + protected function set_view_totals($plugins) + { + foreach ($plugins as $type => $list) { + $this->view_totals[$type] = count($list); + } + } + + /** + * Get the plugin required/recommended text string. + * + * @since 2.5.0 + * + * @param string $required Plugin required setting. + * @return string + */ + protected function get_plugin_advise_type_text($required) + { + if (true === $required) { + return __('Required', 'tgmpa'); + } + + return __('Recommended', 'tgmpa'); + } + + /** + * Get the plugin source type text string. + * + * @since 2.5.0 + * + * @param string $type Plugin type. + * @return string + */ + protected function get_plugin_source_type_text($type) + { + $string = ''; + + switch ($type) { + case 'repo': + $string = __('WordPress Repository', 'tgmpa'); + break; + case 'external': + $string = __('External Source', 'tgmpa'); + break; + case 'bundled': + $string = __('Pre-Packaged', 'tgmpa'); + break; + } + + return $string; + } + + /** + * Determine the plugin status message. + * + * @since 2.5.0 + * + * @param string $slug Plugin slug. + * @return string + */ + protected function get_plugin_status_text($slug) + { + if (!$this->tgmpa->is_plugin_installed($slug)) { + return __('Not Installed', 'tgmpa'); + } + + if (!$this->tgmpa->is_plugin_active($slug)) { + $install_status = __('Installed But Not Activated', 'tgmpa'); + } else { + $install_status = __('Active', 'tgmpa'); + } + + $update_status = ''; + + if ($this->tgmpa->does_plugin_require_update($slug) && false === $this->tgmpa->does_plugin_have_update($slug)) { + $update_status = __('Required Update not Available', 'tgmpa'); + } elseif ($this->tgmpa->does_plugin_require_update($slug)) { + $update_status = __('Requires Update', 'tgmpa'); + } elseif (false !== $this->tgmpa->does_plugin_have_update($slug)) { + $update_status = __('Update recommended', 'tgmpa'); + } + + if ('' === $update_status) { + return $install_status; + } + + return sprintf( + /* translators: 1: install status, 2: update status */ + _x('%1$s, %2$s', 'Install/Update Status', 'tgmpa'), + $install_status, + $update_status + ); + } + + /** + * Sort plugins by Required/Recommended type and by alphabetical plugin name within each type. + * + * @since 2.5.0 + * + * @param array $items Prepared table items. + * @return array Sorted table items. + */ + public function sort_table_items($items) + { + $type = array(); + $name = array(); + + foreach ($items as $i => $plugin) { + $type[$i] = $plugin['type']; // Required / recommended. + $name[$i] = $plugin['sanitized_plugin']; + } + + array_multisort($type, SORT_DESC, $name, SORT_ASC, $items); + + return $items; + } + + /** + * Get an associative array ( id => link ) of the views available on this table. + * + * @since 2.5.0 + * + * @return array + */ + public function get_views() + { + $status_links = array(); + + foreach ($this->view_totals as $type => $count) { + if ($count < 1) { + continue; + } + + switch ($type) { + case 'all': + /* translators: 1: number of plugins. */ + $text = _nx('All (%s)', 'All (%s)', $count, 'plugins', 'tgmpa'); + break; + case 'install': + /* translators: 1: number of plugins. */ + $text = _n('To Install (%s)', 'To Install (%s)', $count, 'tgmpa'); + break; + case 'update': + /* translators: 1: number of plugins. */ + $text = _n('Update Available (%s)', 'Update Available (%s)', $count, 'tgmpa'); + break; + case 'activate': + /* translators: 1: number of plugins. */ + $text = _n('To Activate (%s)', 'To Activate (%s)', $count, 'tgmpa'); + break; + default: + $text = ''; + break; + } + + if (!empty($text)) { + + $status_links[$type] = sprintf( + '%s', + esc_url($this->tgmpa->get_tgmpa_status_url($type)), + ($type === $this->view_context) ? ' class="current"' : '', + sprintf($text, number_format_i18n($count)) + ); + } + } + + return $status_links; + } + + /** + * Create default columns to display important plugin information + * like type, action and status. + * + * @since 2.2.0 + * + * @param array $item Array of item data. + * @param string $column_name The name of the column. + * @return string + */ + public function column_default($item, $column_name) + { + return $item[$column_name]; + } + + /** + * Required for bulk installing. + * + * Adds a checkbox for each plugin. + * + * @since 2.2.0 + * + * @param array $item Array of item data. + * @return string The input checkbox with all necessary info. + */ + public function column_cb($item) + { + return sprintf( + '', + esc_attr($this->_args['singular']), + esc_attr($item['slug']), + esc_attr($item['sanitized_plugin']) + ); + } + + /** + * Create default title column along with the action links. + * + * @since 2.2.0 + * + * @param array $item Array of item data. + * @return string The plugin name and action links. + */ + public function column_plugin($item) + { + return sprintf( + '%1$s %2$s', + $item['plugin'], + $this->row_actions($this->get_row_actions($item), true) + ); + } + + /** + * Create version information column. + * + * @since 2.5.0 + * + * @param array $item Array of item data. + * @return string HTML-formatted version information. + */ + public function column_version($item) + { + $output = array(); + + if ($this->tgmpa->is_plugin_installed($item['slug'])) { + $installed = !empty($item['installed_version']) ? $item['installed_version'] : _x('unknown', 'as in: "version nr unknown"', 'tgmpa'); + + $color = ''; + if (!empty($item['minimum_version']) && $this->tgmpa->does_plugin_require_update($item['slug'])) { + $color = ' color: #ff0000; font-weight: bold;'; + } + + $output[] = sprintf( + '

%2$s' . __('Installed version:', 'tgmpa') . '

', + $color, + $installed + ); + } + + if (!empty($item['minimum_version'])) { + $output[] = sprintf( + '

%1$s' . __('Minimum required version:', 'tgmpa') . '

', + $item['minimum_version'] + ); + } + + if (!empty($item['available_version'])) { + $color = ''; + if (!empty($item['minimum_version']) && version_compare($item['available_version'], $item['minimum_version'], '>=')) { + $color = ' color: #71C671; font-weight: bold;'; + } + + $output[] = sprintf( + '

%2$s' . __('Available version:', 'tgmpa') . '

', + $color, + $item['available_version'] + ); + } + + if (empty($output)) { + return ' '; // Let's not break the table layout. + } else { + return implode("\n", $output); + } + } + + /** + * Sets default message within the plugins table if no plugins + * are left for interaction. + * + * Hides the menu item to prevent the user from clicking and + * getting a permissions error. + * + * @since 2.2.0 + */ + public function no_items() + { + echo esc_html__('No plugins to install, update or activate.', 'tgmpa') . ' ' . esc_html($this->tgmpa->strings['dashboard']) . ''; + echo ''; + } + + /** + * Output all the column information within the table. + * + * @since 2.2.0 + * + * @return array $columns The column names. + */ + public function get_columns() + { + $columns = array( + 'cb' => '', + 'plugin' => __('Plugin', 'tgmpa'), + 'source' => __('Source', 'tgmpa'), + 'type' => __('Type', 'tgmpa'), + ); + + if ('all' === $this->view_context || 'update' === $this->view_context) { + $columns['version'] = __('Version', 'tgmpa'); + $columns['status'] = __('Status', 'tgmpa'); + } + + return apply_filters('tgmpa_table_columns', $columns); + } + + /** + * Get name of default primary column + * + * @since 2.5.0 / WP 4.3+ compatibility + * @access protected + * + * @return string + */ + protected function get_default_primary_column_name() + { + return 'plugin'; + } + + /** + * Get the name of the primary column. + * + * @since 2.5.0 / WP 4.3+ compatibility + * @access protected + * + * @return string The name of the primary column. + */ + protected function get_primary_column_name() + { + if (method_exists('WP_List_Table', 'get_primary_column_name')) { + return parent::get_primary_column_name(); + } else { + return $this->get_default_primary_column_name(); + } + } + + /** + * Get the actions which are relevant for a specific plugin row. + * + * @since 2.5.0 + * + * @param array $item Array of item data. + * @return array Array with relevant action links. + */ + protected function get_row_actions($item) + { + $actions = array(); + $action_links = array(); + + // Display the 'Install' action link if the plugin is not yet available. + if (!$this->tgmpa->is_plugin_installed($item['slug'])) { + /* translators: %2$s: plugin name in screen reader markup */ + $actions['install'] = __('Install %2$s', 'tgmpa'); + } else { + // Display the 'Update' action link if an update is available and WP complies with plugin minimum. + if (false !== $this->tgmpa->does_plugin_have_update($item['slug']) && $this->tgmpa->can_plugin_update($item['slug'])) { + /* translators: %2$s: plugin name in screen reader markup */ + $actions['update'] = __('Update %2$s', 'tgmpa'); + } + + // Display the 'Activate' action link, but only if the plugin meets the minimum version. + if ($this->tgmpa->can_plugin_activate($item['slug'])) { + /* translators: %2$s: plugin name in screen reader markup */ + $actions['activate'] = __('Activate %2$s', 'tgmpa'); + } + } + + // Create the actual links. + foreach ($actions as $action => $text) { + $nonce_url = wp_nonce_url( + add_query_arg( + array( + 'plugin' => urlencode($item['slug']), + 'tgmpa-' . $action => $action . '-plugin', + ), + $this->tgmpa->get_tgmpa_url() + ), + 'tgmpa-' . $action, + 'tgmpa-nonce' + ); + + $action_links[$action] = sprintf( + '' . esc_html($text) . '', // $text contains the second placeholder. + esc_url($nonce_url), + '' . esc_html($item['sanitized_plugin']) . '' + ); + } + + $prefix = (defined('WP_NETWORK_ADMIN') && WP_NETWORK_ADMIN) ? 'network_admin_' : ''; + return apply_filters("tgmpa_{$prefix}plugin_action_links", array_filter($action_links), $item['slug'], $item, $this->view_context); + } + + /** + * Generates content for a single row of the table. + * + * @since 2.5.0 + * + * @param object $item The current item. + */ + public function single_row($item) + { + echo ''; + $this->single_row_columns($item); + echo ''; + + /** + * Fires after each specific row in the TGMPA Plugins list table. + * + * The dynamic portion of the hook name, `$item['slug']`, refers to the slug + * for the plugin. + * + * @since 2.5.0 + */ + do_action("tgmpa_after_plugin_row_{$item['slug']}", $item['slug'], $item, $this->view_context); + } + + /** + * Show the upgrade notice below a plugin row if there is one. + * + * @since 2.5.0 + * + * @see /wp-admin/includes/update.php + * + * @param string $slug Plugin slug. + * @param array $item The information available in this table row. + * @return null Return early if upgrade notice is empty. + */ + public function wp_plugin_update_row($slug, $item) + { + if (empty($item['upgrade_notice'])) { + return; + } + + echo ' + + + '; + } + + /** + * Extra controls to be displayed between bulk actions and pagination. + * + * @since 2.5.0 + * + * @param string $which Either 'top' or 'bottom' table navigation. + */ + public function extra_tablenav($which) + { + if ('bottom' === $which) { + $this->tgmpa->show_tgmpa_version(); + } + } + + /** + * Defines the bulk actions for handling registered plugins. + * + * @since 2.2.0 + * + * @return array $actions The bulk actions for the plugin install table. + */ + public function get_bulk_actions() + { + + $actions = array(); + + if ('update' !== $this->view_context && 'activate' !== $this->view_context) { + if (current_user_can('install_plugins')) { + $actions['tgmpa-bulk-install'] = __('Install', 'tgmpa'); + } + } + + if ('install' !== $this->view_context) { + if (current_user_can('update_plugins')) { + $actions['tgmpa-bulk-update'] = __('Update', 'tgmpa'); + } + if (current_user_can('activate_plugins')) { + $actions['tgmpa-bulk-activate'] = __('Activate', 'tgmpa'); + } + } + + return $actions; + } + + /** + * Processes bulk installation and activation actions. + * + * The bulk installation process looks for the $_POST information and passes that + * through if a user has to use WP_Filesystem to enter their credentials. + * + * @since 2.2.0 + */ + public function process_bulk_actions() + { + // Bulk installation process. + if ('tgmpa-bulk-install' === $this->current_action() || 'tgmpa-bulk-update' === $this->current_action()) { + + check_admin_referer('bulk-' . $this->_args['plural']); + + $install_type = 'install'; + if ('tgmpa-bulk-update' === $this->current_action()) { + $install_type = 'update'; + } + + $plugins_to_install = array(); + + // Did user actually select any plugins to install/update ? + if (empty($_POST['plugin'])) { + if ('install' === $install_type) { + $message = __('No plugins were selected to be installed. No action taken.', 'tgmpa'); + } else { + $message = __('No plugins were selected to be updated. No action taken.', 'tgmpa'); + } + + echo '

', esc_html($message), '

'; + + return false; + } + + if (is_array($_POST['plugin'])) { + $plugins_to_install = (array) $_POST['plugin']; + } elseif (is_string($_POST['plugin'])) { + // Received via Filesystem page - un-flatten array (WP bug #19643). + $plugins_to_install = explode(',', $_POST['plugin']); + } + + // Sanitize the received input. + $plugins_to_install = array_map('urldecode', $plugins_to_install); + $plugins_to_install = array_map(array($this->tgmpa, 'sanitize_key'), $plugins_to_install); + + // Validate the received input. + foreach ($plugins_to_install as $key => $slug) { + // Check if the plugin was registered with TGMPA and remove if not. + if (!isset($this->tgmpa->plugins[$slug])) { + unset($plugins_to_install[$key]); + continue; + } + + // For install: make sure this is a plugin we *can* install and not one already installed. + if ('install' === $install_type && true === $this->tgmpa->is_plugin_installed($slug)) { + unset($plugins_to_install[$key]); + } + + // For updates: make sure this is a plugin we *can* update (update available and WP version ok). + if ('update' === $install_type && false === $this->tgmpa->is_plugin_updatetable($slug)) { + unset($plugins_to_install[$key]); + } + } + + // No need to proceed further if we have no plugins to handle. + if (empty($plugins_to_install)) { + if ('install' === $install_type) { + $message = __('No plugins are available to be installed at this time.', 'tgmpa'); + } else { + $message = __('No plugins are available to be updated at this time.', 'tgmpa'); + } + + echo '

', esc_html($message), '

'; + + return false; + } + + // Pass all necessary information if WP_Filesystem is needed. + $url = wp_nonce_url( + $this->tgmpa->get_tgmpa_url(), + 'bulk-' . $this->_args['plural'] + ); + + // Give validated data back to $_POST which is the only place the filesystem looks for extra fields. + $_POST['plugin'] = implode(',', $plugins_to_install); // Work around for WP bug #19643. + + $method = ''; // Leave blank so WP_Filesystem can populate it as necessary. + $fields = array_keys($_POST); // Extra fields to pass to WP_Filesystem. + + $creds = request_filesystem_credentials(esc_url_raw($url), $method, false, false, $fields); + if (false === $creds) { + return true; // Stop the normal page form from displaying, credential request form will be shown. + } + + // Now we have some credentials, setup WP_Filesystem. + if (!WP_Filesystem($creds)) { + // Our credentials were no good, ask the user for them again. + request_filesystem_credentials(esc_url_raw($url), $method, true, false, $fields); + + return true; + } + + /* If we arrive here, we have the filesystem */ + + // Store all information in arrays since we are processing a bulk installation. + $names = array(); + $sources = array(); // Needed for installs. + $file_paths = array(); // Needed for upgrades. + $to_inject = array(); // Information to inject into the update_plugins transient. + + // Prepare the data for validated plugins for the install/upgrade. + foreach ($plugins_to_install as $slug) { + $name = $this->tgmpa->plugins[$slug]['name']; + $source = $this->tgmpa->get_download_url($slug); + + if (!empty($name) && !empty($source)) { + $names[] = $name; + + switch ($install_type) { + + case 'install': + $sources[] = $source; + break; + + case 'update': + $file_paths[] = $this->tgmpa->plugins[$slug]['file_path']; + $to_inject[$slug] = $this->tgmpa->plugins[$slug]; + $to_inject[$slug]['source'] = $source; + break; + } + } + } + unset($slug, $name, $source); + + // Create a new instance of TGMPA_Bulk_Installer. + $installer = new TGMPA_Bulk_Installer( + new TGMPA_Bulk_Installer_Skin( + array( + 'url' => esc_url_raw($this->tgmpa->get_tgmpa_url()), + 'nonce' => 'bulk-' . $this->_args['plural'], + 'names' => $names, + 'install_type' => $install_type, + ) + ) + ); + + // Wrap the install process with the appropriate HTML. + echo '
', + '

', esc_html(get_admin_page_title()), '

+
'; + + // Process the bulk installation submissions. + add_filter('upgrader_source_selection', array($this->tgmpa, 'maybe_adjust_source_dir'), 1, 3); + + if ('tgmpa-bulk-update' === $this->current_action()) { + // Inject our info into the update transient. + $this->tgmpa->inject_update_info($to_inject); + + $installer->bulk_upgrade($file_paths); + } else { + $installer->bulk_install($sources); + } + + remove_filter('upgrader_source_selection', array($this->tgmpa, 'maybe_adjust_source_dir'), 1); + + echo '
'; + + return true; + } + + // Bulk activation process. + if ('tgmpa-bulk-activate' === $this->current_action()) { + check_admin_referer('bulk-' . $this->_args['plural']); + + // Did user actually select any plugins to activate ? + if (empty($_POST['plugin'])) { + echo '

', esc_html__('No plugins were selected to be activated. No action taken.', 'tgmpa'), '

'; + + return false; + } + + // Grab plugin data from $_POST. + $plugins = array(); + if (isset($_POST['plugin'])) { + $plugins = array_map('urldecode', (array) $_POST['plugin']); + $plugins = array_map(array($this->tgmpa, 'sanitize_key'), $plugins); + } + + $plugins_to_activate = array(); + $plugin_names = array(); + + // Grab the file paths for the selected & inactive plugins from the registration array. + foreach ($plugins as $slug) { + if ($this->tgmpa->can_plugin_activate($slug)) { + $plugins_to_activate[] = $this->tgmpa->plugins[$slug]['file_path']; + $plugin_names[] = $this->tgmpa->plugins[$slug]['name']; + } + } + unset($slug); + + // Return early if there are no plugins to activate. + if (empty($plugins_to_activate)) { + echo '

', esc_html__('No plugins are available to be activated at this time.', 'tgmpa'), '

'; + + return false; + } + + // Now we are good to go - let's start activating plugins. + $activate = activate_plugins($plugins_to_activate); + + if (is_wp_error($activate)) { + echo '

', wp_kses_post($activate->get_error_message()), '

'; + } else { + $count = count($plugin_names); // Count so we can use _n function. + $plugin_names = array_map(array('TGMPA_Utils', 'wrap_in_strong'), $plugin_names); + $last_plugin = array_pop($plugin_names); // Pop off last name to prep for readability. + $imploded = empty($plugin_names) ? $last_plugin : (implode(', ', $plugin_names) . ' ' . esc_html_x('and', 'plugin A *and* plugin B', 'tgmpa') . ' ' . $last_plugin); + + printf( + '

%1$s %2$s.

', + esc_html(_n('The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, 'tgmpa')), + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped via wrap_in_strong() method above. + $imploded + ); + + // Update recently activated plugins option. + $recent = (array) get_option('recently_activated'); + foreach ($plugins_to_activate as $plugin => $time) { + if (isset($recent[$plugin])) { + unset($recent[$plugin]); + } + } + update_option('recently_activated', $recent); + } + + unset($_POST); // Reset the $_POST variable in case user wants to perform one action after another. + + return true; + } + + return false; + } + + /** + * Prepares all of our information to be outputted into a usable table. + * + * @since 2.2.0 + */ + public function prepare_items() + { + $columns = $this->get_columns(); // Get all necessary column information. + $hidden = array(); // No columns to hide, but we must set as an array. + $sortable = array(); // No reason to make sortable columns. + $primary = $this->get_primary_column_name(); // Column which has the row actions. + $this->_column_headers = array($columns, $hidden, $sortable, $primary); // Get all necessary column headers. + + // Process our bulk activations here. + if ('tgmpa-bulk-activate' === $this->current_action()) { + $this->process_bulk_actions(); + } + + // Store all of our plugin data into $items array so WP_List_Table can use it. + $this->items = apply_filters('tgmpa_table_data_items', $this->_gather_plugin_data()); + } + + /* *********** DEPRECATED METHODS *********** */ + + /** + * Retrieve plugin data, given the plugin name. + * + * @since 2.2.0 + * @deprecated 2.5.0 use {@see TGM_Plugin_Activation::_get_plugin_data_from_name()} instead. + * @see TGM_Plugin_Activation::_get_plugin_data_from_name() + * + * @param string $name Name of the plugin, as it was registered. + * @param string $data Optional. Array key of plugin data to return. Default is slug. + * @return string|boolean Plugin slug if found, false otherwise. + */ + protected function _get_plugin_data_from_name($name, $data = 'slug') + { + _deprecated_function(__FUNCTION__, 'TGMPA 2.5.0', 'TGM_Plugin_Activation::_get_plugin_data_from_name()'); + + return $this->tgmpa->_get_plugin_data_from_name($name, $data); + } + } +} + + +if (!class_exists('TGM_Bulk_Installer')) { + + /** + * Hack: Prevent TGMPA v2.4.1- bulk installer class from being loaded if 2.4.1- is loaded after 2.5+. + * + * @since 2.5.2 + * + * {@internal The TGMPA_Bulk_Installer class was originally called TGM_Bulk_Installer. + * For more information, see that class.}} + */ + class TGM_Bulk_Installer + { + } +} +if (!class_exists('TGM_Bulk_Installer_Skin')) { + + /** + * Hack: Prevent TGMPA v2.4.1- bulk installer skin class from being loaded if 2.4.1- is loaded after 2.5+. + * + * @since 2.5.2 + * + * {@internal The TGMPA_Bulk_Installer_Skin class was originally called TGM_Bulk_Installer_Skin. + * For more information, see that class.}} + */ + class TGM_Bulk_Installer_Skin + { + } +} + +/** + * The WP_Upgrader file isn't always available. If it isn't available, + * we load it here. + * + * We check to make sure no action or activation keys are set so that WordPress + * does not try to re-include the class when processing upgrades or installs outside + * of the class. + * + * @since 2.2.0 + */ +add_action('admin_init', 'tgmpa_load_bulk_installer'); +if (!function_exists('tgmpa_load_bulk_installer')) { + /** + * Load bulk installer + */ + function tgmpa_load_bulk_installer() + { + // Silently fail if 2.5+ is loaded *after* an older version. + if (!isset($GLOBALS['tgmpa'])) { + return; + } + + // Get TGMPA class instance. + $tgmpa_instance = call_user_func(array(get_class($GLOBALS['tgmpa']), 'get_instance')); + + if (isset($_GET['page']) && $tgmpa_instance->menu === $_GET['page']) { + if (!class_exists('Plugin_Upgrader', false)) { + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + } + + if (!class_exists('TGMPA_Bulk_Installer')) { + + /** + * Installer class to handle bulk plugin installations. + * + * Extends WP_Upgrader and customizes to suit the installation of multiple + * plugins. + * + * @since 2.2.0 + * + * {@internal Since 2.5.0 the class is an extension of Plugin_Upgrader rather than WP_Upgrader.}} + * {@internal Since 2.5.2 the class has been renamed from TGM_Bulk_Installer to TGMPA_Bulk_Installer. + * This was done to prevent backward compatibility issues with v2.3.6.}} + * + * @package TGM-Plugin-Activation + * @author Thomas Griffin + * @author Gary Jones + */ + class TGMPA_Bulk_Installer extends Plugin_Upgrader + { + /** + * Holds result of bulk plugin installation. + * + * @since 2.2.0 + * + * @var string + */ + public $result; + + /** + * Flag to check if bulk installation is occurring or not. + * + * @since 2.2.0 + * + * @var boolean + */ + public $bulk = false; + + /** + * TGMPA instance + * + * @since 2.5.0 + * + * @var object + */ + protected $tgmpa; + + /** + * Whether or not the destination directory needs to be cleared ( = on update). + * + * @since 2.5.0 + * + * @var bool + */ + protected $clear_destination = false; + + /** + * References parent constructor and sets defaults for class. + * + * @since 2.2.0 + * + * @param \Bulk_Upgrader_Skin|null $skin Installer skin. + */ + public function __construct($skin = null) + { + // Get TGMPA class instance. + $this->tgmpa = call_user_func(array(get_class($GLOBALS['tgmpa']), 'get_instance')); + + parent::__construct($skin); + + if (isset($this->skin->options['install_type']) && 'update' === $this->skin->options['install_type']) { + $this->clear_destination = true; + } + + if ($this->tgmpa->is_automatic) { + $this->activate_strings(); + } + + add_action('upgrader_process_complete', array($this->tgmpa, 'populate_file_path')); + } + + /** + * Sets the correct activation strings for the installer skin to use. + * + * @since 2.2.0 + */ + public function activate_strings() + { + $this->strings['activation_failed'] = __('Plugin activation failed.', 'tgmpa'); + $this->strings['activation_success'] = __('Plugin activated successfully.', 'tgmpa'); + } + + /** + * Performs the actual installation of each plugin. + * + * @since 2.2.0 + * + * @see WP_Upgrader::run() + * + * @param array $options The installation config options. + * @return null|array Return early if error, array of installation data on success. + */ + public function run($options) + { + $result = parent::run($options); + + // Reset the strings in case we changed one during automatic activation. + if ($this->tgmpa->is_automatic) { + if ('update' === $this->skin->options['install_type']) { + $this->upgrade_strings(); + } else { + $this->install_strings(); + } + } + + return $result; + } + + /** + * Processes the bulk installation of plugins. + * + * @since 2.2.0 + * + * {@internal This is basically a near identical copy of the WP Core + * Plugin_Upgrader::bulk_upgrade() method, with minor adjustments to deal with + * new installs instead of upgrades. + * For ease of future synchronizations, the adjustments are clearly commented, but no other + * comments are added. Code style has been made to comply.}} + * + * @see Plugin_Upgrader::bulk_upgrade() + * @see https://core.trac.wordpress.org/browser/tags/4.2.1/src/wp-admin/includes/class-wp-upgrader.php#L838 + * (@internal Last synced: Dec 31st 2015 against https://core.trac.wordpress.org/browser/trunk?rev=36134}} + * + * @param array $plugins The plugin sources needed for installation. + * @param array $args Arbitrary passed extra arguments. + * @return array|false Install confirmation messages on success, false on failure. + */ + public function bulk_install($plugins, $args = array()) + { + // [TGMPA + ] Hook auto-activation in. + add_filter('upgrader_post_install', array($this, 'auto_activate'), 10); + + $defaults = array( + 'clear_update_cache' => true, + ); + $parsed_args = wp_parse_args($args, $defaults); + + $this->init(); + $this->bulk = true; + + $this->install_strings(); // [TGMPA + ] adjusted. + + /* [TGMPA - ] $current = get_site_transient( 'update_plugins' ); */ + + /* [TGMPA - ] add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); */ + + $this->skin->header(); + + // Connect to the Filesystem first. + $res = $this->fs_connect(array(WP_CONTENT_DIR, WP_PLUGIN_DIR)); + if (!$res) { + $this->skin->footer(); + return false; + } + + $this->skin->bulk_header(); + + /* + * Only start maintenance mode if: + * - running Multisite and there are one or more plugins specified, OR + * - a plugin with an update available is currently active. + * @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible. + */ + $maintenance = (is_multisite() && !empty($plugins)); + + /* + [TGMPA - ] + foreach ( $plugins as $plugin ) + $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) ); + */ + if ($maintenance) { + $this->maintenance_mode(true); + } + + $results = array(); + + $this->update_count = count($plugins); + $this->update_current = 0; + foreach ($plugins as $plugin) { + $this->update_current++; + + /* + [TGMPA - ] + $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true); + + if ( !isset( $current->response[ $plugin ] ) ) { + $this->skin->set_result('up_to_date'); + $this->skin->before(); + $this->skin->feedback('up_to_date'); + $this->skin->after(); + $results[$plugin] = true; + continue; + } + + // Get the URL to the zip file. + $r = $current->response[ $plugin ]; + + $this->skin->plugin_active = is_plugin_active($plugin); + */ + + $result = $this->run( + array( + 'package' => $plugin, // [TGMPA + ] adjusted. + 'destination' => WP_PLUGIN_DIR, + 'clear_destination' => false, // [TGMPA + ] adjusted. + 'clear_working' => true, + 'is_multi' => true, + 'hook_extra' => array( + 'plugin' => $plugin, + ), + ) + ); + + $results[$plugin] = $this->result; + + // Prevent credentials auth screen from displaying multiple times. + if (false === $result) { + break; + } + } + + $this->maintenance_mode(false); + + /** + * Fires when the bulk upgrader process is complete. + * + * @since WP 3.6.0 / TGMPA 2.5.0 + * + * @param Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might + * be a Theme_Upgrader or Core_Upgrade instance. + * @param array $data { + * Array of bulk item update data. + * + * @type string $action Type of action. Default 'update'. + * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'. + * @type bool $bulk Whether the update process is a bulk update. Default true. + * @type array $packages Array of plugin, theme, or core packages to update. + * } + */ + do_action( + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using WP core hook. + 'upgrader_process_complete', + $this, + array( + 'action' => 'install', // [TGMPA + ] adjusted. + 'type' => 'plugin', + 'bulk' => true, + 'plugins' => $plugins, + ) + ); + + $this->skin->bulk_footer(); + + $this->skin->footer(); + + // Cleanup our hooks, in case something else does a upgrade on this connection. + /* [TGMPA - ] remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); */ + + // [TGMPA + ] Remove our auto-activation hook. + remove_filter('upgrader_post_install', array($this, 'auto_activate'), 10); + + // Force refresh of plugin update information. + wp_clean_plugins_cache($parsed_args['clear_update_cache']); + + return $results; + } + + /** + * Handle a bulk upgrade request. + * + * @since 2.5.0 + * + * @see Plugin_Upgrader::bulk_upgrade() + * + * @param array $plugins The local WP file_path's of the plugins which should be upgraded. + * @param array $args Arbitrary passed extra arguments. + * @return string|bool Install confirmation messages on success, false on failure. + */ + public function bulk_upgrade($plugins, $args = array()) + { + + add_filter('upgrader_post_install', array($this, 'auto_activate'), 10); + + $result = parent::bulk_upgrade($plugins, $args); + + remove_filter('upgrader_post_install', array($this, 'auto_activate'), 10); + + return $result; + } + + /** + * Abuse a filter to auto-activate plugins after installation. + * + * Hooked into the 'upgrader_post_install' filter hook. + * + * @since 2.5.0 + * + * @param bool $bool The value we need to give back (true). + * @return bool + */ + public function auto_activate($bool) + { + // Only process the activation of installed plugins if the automatic flag is set to true. + if ($this->tgmpa->is_automatic) { + // Flush plugins cache so the headers of the newly installed plugins will be read correctly. + wp_clean_plugins_cache(); + + // Get the installed plugin file. + $plugin_info = $this->plugin_info(); + + // Don't try to activate on upgrade of active plugin as WP will do this already. + if (!is_plugin_active($plugin_info)) { + $activate = activate_plugin($plugin_info); + + // Adjust the success string based on the activation result. + $this->strings['process_success'] = $this->strings['process_success'] . "
\n"; + + if (is_wp_error($activate)) { + $this->skin->error($activate); + $this->strings['process_success'] .= $this->strings['activation_failed']; + } else { + $this->strings['process_success'] .= $this->strings['activation_success']; + } + } + } + + return $bool; + } + } + } + + if (!class_exists('TGMPA_Bulk_Installer_Skin')) { + + /** + * Installer skin to set strings for the bulk plugin installations.. + * + * Extends Bulk_Upgrader_Skin and customizes to suit the installation of multiple + * plugins. + * + * @since 2.2.0 + * + * {@internal Since 2.5.2 the class has been renamed from TGM_Bulk_Installer_Skin to + * TGMPA_Bulk_Installer_Skin. + * This was done to prevent backward compatibility issues with v2.3.6.}} + * + * @see https://core.trac.wordpress.org/browser/trunk/src/wp-admin/includes/class-wp-upgrader-skins.php + * + * @package TGM-Plugin-Activation + * @author Thomas Griffin + * @author Gary Jones + */ + class TGMPA_Bulk_Installer_Skin extends Bulk_Upgrader_Skin + { + /** + * Holds plugin info for each individual plugin installation. + * + * @since 2.2.0 + * + * @var array + */ + public $plugin_info = array(); + + /** + * Holds names of plugins that are undergoing bulk installations. + * + * @since 2.2.0 + * + * @var array + */ + public $plugin_names = array(); + + /** + * Integer to use for iteration through each plugin installation. + * + * @since 2.2.0 + * + * @var integer + */ + public $i = 0; + + /** + * TGMPA instance + * + * @since 2.5.0 + * + * @var object + */ + protected $tgmpa; + + /** + * Constructor. Parses default args with new ones and extracts them for use. + * + * @since 2.2.0 + * + * @param array $args Arguments to pass for use within the class. + */ + public function __construct($args = array()) + { + // Get TGMPA class instance. + $this->tgmpa = call_user_func(array(get_class($GLOBALS['tgmpa']), 'get_instance')); + + // Parse default and new args. + $defaults = array( + 'url' => '', + 'nonce' => '', + 'names' => array(), + 'install_type' => 'install', + ); + $args = wp_parse_args($args, $defaults); + + // Set plugin names to $this->plugin_names property. + $this->plugin_names = $args['names']; + + // Extract the new args. + parent::__construct($args); + } + + /** + * Sets install skin strings for each individual plugin. + * + * Checks to see if the automatic activation flag is set and uses the + * the proper strings accordingly. + * + * @since 2.2.0 + */ + public function add_strings() + { + if ('update' === $this->options['install_type']) { + parent::add_strings(); + /* translators: 1: plugin name, 2: action number 3: total number of actions. */ + $this->upgrader->strings['skin_before_update_header'] = __('Updating Plugin %1$s (%2$d/%3$d)', 'tgmpa'); + } else { + /* translators: 1: plugin name, 2: error message. */ + $this->upgrader->strings['skin_update_failed_error'] = __('An error occurred while installing %1$s: %2$s.', 'tgmpa'); + /* translators: 1: plugin name. */ + $this->upgrader->strings['skin_update_failed'] = __('The installation of %1$s failed.', 'tgmpa'); + + if ($this->tgmpa->is_automatic) { + // Automatic activation strings. + $this->upgrader->strings['skin_upgrade_start'] = __('The installation and activation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa'); + /* translators: 1: plugin name. */ + $this->upgrader->strings['skin_update_successful'] = __('%1$s installed and activated successfully.', 'tgmpa'); + $this->upgrader->strings['skin_upgrade_end'] = __('All installations and activations have been completed.', 'tgmpa'); + /* translators: 1: plugin name, 2: action number 3: total number of actions. */ + $this->upgrader->strings['skin_before_update_header'] = __('Installing and Activating Plugin %1$s (%2$d/%3$d)', 'tgmpa'); + } else { + // Default installation strings. + $this->upgrader->strings['skin_upgrade_start'] = __('The installation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa'); + /* translators: 1: plugin name. */ + $this->upgrader->strings['skin_update_successful'] = __('%1$s installed successfully.', 'tgmpa'); + $this->upgrader->strings['skin_upgrade_end'] = __('All installations have been completed.', 'tgmpa'); + /* translators: 1: plugin name, 2: action number 3: total number of actions. */ + $this->upgrader->strings['skin_before_update_header'] = __('Installing Plugin %1$s (%2$d/%3$d)', 'tgmpa'); + } + + // Add "read more" link only for WP < 4.8. + if (version_compare($this->tgmpa->wp_version, '4.8', '<')) { + $this->upgrader->strings['skin_update_successful'] .= ' ' . esc_html__('Show Details', 'tgmpa') . '.'; + } + } + } + + /** + * Outputs the header strings and necessary JS before each plugin installation. + * + * @since 2.2.0 + * + * @param string $title Unused in this implementation. + */ + public function before($title = '') + { + if (empty($title)) { + $title = esc_html($this->plugin_names[$this->i]); + } + parent::before($title); + } + + /** + * Outputs the footer strings and necessary JS after each plugin installation. + * + * Checks for any errors and outputs them if they exist, else output + * success strings. + * + * @since 2.2.0 + * + * @param string $title Unused in this implementation. + */ + public function after($title = '') + { + if (empty($title)) { + $title = esc_html($this->plugin_names[$this->i]); + } + parent::after($title); + + $this->i++; + } + + /** + * Outputs links after bulk plugin installation is complete. + * + * @since 2.2.0 + */ + public function bulk_footer() + { + // Serve up the string to say installations (and possibly activations) are complete. + parent::bulk_footer(); + + // Flush plugins cache so we can make sure that the installed plugins list is always up to date. + wp_clean_plugins_cache(); + + $this->tgmpa->show_tgmpa_version(); + + // Display message based on if all plugins are now active or not. + $update_actions = array(); + + if ($this->tgmpa->is_tgmpa_complete()) { + // All plugins are active, so we display the complete string and hide the menu to protect users. + echo ''; + $update_actions['dashboard'] = sprintf( + esc_html($this->tgmpa->strings['complete']), + '' . esc_html($this->tgmpa->strings['dashboard']) . '' + ); + } else { + $update_actions['tgmpa_page'] = '' . esc_html($this->tgmpa->strings['return']) . ''; + } + + /** + * Filter the list of action links available following bulk plugin installs/updates. + * + * @since 2.5.0 + * + * @param array $update_actions Array of plugin action links. + * @param array $plugin_info Array of information for the last-handled plugin. + */ + $update_actions = apply_filters('tgmpa_update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info); + + if (!empty($update_actions)) { + $this->feedback(implode(' | ', (array) $update_actions)); + } + } + + /* *********** DEPRECATED METHODS *********** */ + + /** + * Flush header output buffer. + * + * @since 2.2.0 + * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead + * @see Bulk_Upgrader_Skin::flush_output() + */ + public function before_flush_output() + { + _deprecated_function(__FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()'); + $this->flush_output(); + } + + /** + * Flush footer output buffer and iterate $this->i to make sure the + * installation strings reference the correct plugin. + * + * @since 2.2.0 + * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead + * @see Bulk_Upgrader_Skin::flush_output() + */ + public function after_flush_output() + { + _deprecated_function(__FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()'); + $this->flush_output(); + $this->i++; + } + } + } + } + } +} + +if (!class_exists('TGMPA_Utils')) { + + /** + * Generic utilities for TGMPA. + * + * All methods are static, poor-dev name-spacing class wrapper. + * + * Class was called TGM_Utils in 2.5.0 but renamed TGMPA_Utils in 2.5.1 as this was conflicting with Soliloquy. + * + * @since 2.5.0 + * + * @package TGM-Plugin-Activation + * @author Juliette Reinders Folmer + */ + class TGMPA_Utils + { + /** + * Whether the PHP filter extension is enabled. + * + * @see http://php.net/book.filter + * + * @since 2.5.0 + * + * @static + * + * @var bool $has_filters True is the extension is enabled. + */ + public static $has_filters; + + /** + * Wrap an arbitrary string in tags. Meant to be used in combination with array_map(). + * + * @since 2.5.0 + * + * @static + * + * @param string $string Text to be wrapped. + * @return string + */ + public static function wrap_in_em($string) + { + return '' . wp_kses_post($string) . ''; + } + + /** + * Wrap an arbitrary string in tags. Meant to be used in combination with array_map(). + * + * @since 2.5.0 + * + * @static + * + * @param string $string Text to be wrapped. + * @return string + */ + public static function wrap_in_strong($string) + { + return '' . wp_kses_post($string) . ''; + } + + /** + * Helper function: Validate a value as boolean + * + * @since 2.5.0 + * + * @static + * + * @param mixed $value Arbitrary value. + * @return bool + */ + public static function validate_bool($value) + { + if (!isset(self::$has_filters)) { + self::$has_filters = extension_loaded('filter'); + } + + if (self::$has_filters) { + return filter_var($value, FILTER_VALIDATE_BOOLEAN); + } else { + return self::emulate_filter_bool($value); + } + } + + /** + * Helper function: Cast a value to bool + * + * @since 2.5.0 + * + * @static + * + * @param mixed $value Value to cast. + * @return bool + */ + protected static function emulate_filter_bool($value) + { + // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine + static $true = array( + '1', + 'true', 'True', 'TRUE', + 'y', 'Y', + 'yes', 'Yes', 'YES', + 'on', 'On', 'ON', + ); + static $false = array( + '0', + 'false', 'False', 'FALSE', + 'n', 'N', + 'no', 'No', 'NO', + 'off', 'Off', 'OFF', + ); + // phpcs:enable + + if (is_bool($value)) { + return $value; + } elseif (is_int($value) && (0 === $value || 1 === $value)) { + return (bool) $value; + } elseif ((is_float($value) && !is_nan($value)) && ((float) 0 === $value || (float) 1 === $value)) { + return (bool) $value; + } elseif (is_string($value)) { + $value = trim($value); + if (in_array($value, $true, true)) { + return true; + } elseif (in_array($value, $false, true)) { + return false; + } else { + return false; + } + } + + return false; + } + } // End of class TGMPA_Utils +} diff --git a/footer.php b/footer.php new file mode 100644 index 0000000..9317163 --- /dev/null +++ b/footer.php @@ -0,0 +1,103 @@ + + +
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+ +
+ + + +
+ + + +
+ + + +
+ + +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
Leon Pharmacy Logo + + + {TITLE} + '; + wp_nav_menu( array( + 'container' => '', + 'theme_location' => 'leon_pharmacy', + 'items_wrap' => '
    %3$s
', + 'walker' => new PG_Smart_Walker_Nav_Menu() + ) ); ?> + +
+
+
+

+
+







+
+
+
+

+
    +
  • + +
  • +
  • +
  • +
  • +
+
+
+
+
+

+
+
+
+
+
+ + + diff --git a/front-page.php b/front-page.php new file mode 100644 index 0000000..1872879 --- /dev/null +++ b/front-page.php @@ -0,0 +1,251 @@ + + +
+
+
+
+
+

+


+
+

+
+
+

+ Readers Choice 2023 Diamond Award +
+
+
+
+
+ +
+
+
+
+ +
+
+

.

+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+

+
+
+ SYNMED Blister Pack Robot

+
+
+
+
+
+
+

+
+
+ Leon Pharmacy Entrance +
+

+
+
+
+ Leon Pharmacy Award Wall +
+

+
+
+
+ Leon Pharmacy Interior +
+

+
+
+
+
+
+
+
+

+
+
+
+


+
+

.

+
+
+
+
+
+


+
+

+
+
+
+
+
+
+
+
+

+
+

+
+
+
+ + + + +

+

+
+
+ +
+
+ + + + + +

+

+
+
+ +
+
+ + + + +

+

+
+
+
+
+ + + +

+

+
+
+
+
+ + + + +

+

+
+
+
+
+ + + + + +

+

+
+
+
+
+ + + + + +

+

+
+
+
+
+ + + +

+

+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/functions.php b/functions.php new file mode 100644 index 0000000..b3c1113 --- /dev/null +++ b/functions.php @@ -0,0 +1,256 @@ + __( 'Primary Menu', 'leonspharmacy' ), + 'social' => __( 'Social Links Menu', 'leonspharmacy' ), + ) ); + +/* + * Register custom menu locations + */ + /* Pinegrow generated Register Menus Begin */ + + register_nav_menu( 'primary', __( 'Main menu', 'leonpharmacy' ) ); + + register_nav_menu( 'leon_pharmacy', __( 'Main Footer Menu', 'leonpharmacy' ) ); + + /* Pinegrow generated Register Menus End */ + +/* + * Set image sizes + */ + /* Pinegrow generated Image sizes Begin */ + + /* Pinegrow generated Image sizes End */ + + /* + * Switch default core markup for search form, comment form, and comments + * to output valid HTML5. + */ + add_theme_support( 'html5', array( + 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption' + ) ); + + /* + * Enable support for Post Formats. + */ + add_theme_support( 'post-formats', array( + 'aside', 'image', 'video', 'quote', 'link', 'gallery', 'status', 'audio', 'chat' + ) ); + + /* + * Enable support for Page excerpts. + */ + add_post_type_support( 'page', 'excerpt' ); +} +endif; // leonspharmacy_setup + +add_action( 'after_setup_theme', 'leonspharmacy_setup' ); + + +if ( ! function_exists( 'leonspharmacy_init' ) ) : + +function leonspharmacy_init() { + + + // Use categories and tags with attachments + register_taxonomy_for_object_type( 'category', 'attachment' ); + register_taxonomy_for_object_type( 'post_tag', 'attachment' ); + + /* + * Register custom post types. You can also move this code to a plugin. + */ + /* Pinegrow generated Custom Post Types Begin */ + + register_post_type('contact_message', array( + 'labels' => + array( + 'name' => __( 'Messages', 'leonpharmacy' ), + 'singular_name' => __( 'Message', 'leonpharmacy' ) + ), + 'public' => false, + 'supports' => array( 'title', 'editor', 'author' ), + 'show_in_rest' => false, + 'show_ui' => true, + 'show_in_menu' => true + )); + + /* Pinegrow generated Custom Post Types End */ + + /* + * Register custom taxonomies. You can also move this code to a plugin. + */ + /* Pinegrow generated Taxonomies Begin */ + + /* Pinegrow generated Taxonomies End */ + +} +endif; // leonspharmacy_setup + +add_action( 'init', 'leonspharmacy_init' ); + + +if ( ! function_exists( 'leonspharmacy_custom_image_sizes_names' ) ) : + +function leonspharmacy_custom_image_sizes_names( $sizes ) { + + /* + * Add names of custom image sizes. + */ + /* Pinegrow generated Image Sizes Names Begin*/ + /* This code will be replaced by returning names of custom image sizes. */ + /* Pinegrow generated Image Sizes Names End */ + return $sizes; +} +add_action( 'image_size_names_choose', 'leonspharmacy_custom_image_sizes_names' ); +endif;// leonspharmacy_custom_image_sizes_names + + + +if ( ! function_exists( 'leonspharmacy_widgets_init' ) ) : + +function leonspharmacy_widgets_init() { + + /* + * Register widget areas. + */ + /* Pinegrow generated Register Sidebars Begin */ + + register_sidebar( array( + 'name' => __( 'Archive', 'leonpharmacy' ), + 'id' => 'archive', + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

' + ) ); + + /* Pinegrow generated Register Sidebars End */ +} +add_action( 'widgets_init', 'leonspharmacy_widgets_init' ); +endif;// leonspharmacy_widgets_init + + + +if ( ! function_exists( 'leonspharmacy_customize_register' ) ) : + +function leonspharmacy_customize_register( $wp_customize ) { + // Do stuff with $wp_customize, the WP_Customize_Manager object. + + /* Pinegrow generated Customizer Controls Begin */ + + /* Pinegrow generated Customizer Controls End */ + +} +add_action( 'customize_register', 'leonspharmacy_customize_register' ); +endif;// leonspharmacy_customize_register + + +if ( ! function_exists( 'leonspharmacy_enqueue_scripts' ) ) : + function leonspharmacy_enqueue_scripts() { + + /* Pinegrow generated Enqueue Scripts Begin */ + + wp_deregister_script( 'leonpharmacy-app' ); + wp_enqueue_script( 'leonpharmacy-app', get_template_directory_uri() . '/assets/js/app.js', [], '1.5', true); + + /* Pinegrow generated Enqueue Scripts End */ + + /* Pinegrow generated Enqueue Styles Begin */ + + wp_deregister_style( 'leonpharmacy-styles' ); + wp_enqueue_style( 'leonpharmacy-styles', get_template_directory_uri() . '/assets/css/styles.css', [], '1.5', 'all'); + + wp_deregister_style( 'leonpharmacy-style' ); + wp_enqueue_style( 'leonpharmacy-style', get_bloginfo('stylesheet_url'), [], '1.5', 'all'); + + /* Pinegrow generated Enqueue Styles End */ + + } + add_action( 'wp_enqueue_scripts', 'leonspharmacy_enqueue_scripts' ); +endif; + +function pgwp_sanitize_placeholder($input) { return $input; } +/* + * Resource files included by Pinegrow. + */ +/* Pinegrow generated Include Resources Begin */ +require_once "inc/custom.php"; +if( !class_exists( 'PG_Helper_v2' ) ) { require_once "inc/wp_pg_helpers.php"; } +if( !class_exists( 'PG_Blocks_v2' ) ) { require_once "inc/wp_pg_blocks_helpers.php"; } +if( !class_exists( 'PG_Simple_Form_Mailer' ) ) { require_once "inc/wp_simple_form_mailer.php"; } +if( !class_exists( 'PG_Pagination' ) ) { require_once "inc/wp_pg_pagination.php"; } +if( !class_exists( 'PG_Smart_Walker_Nav_Menu' ) ) { require_once "inc/wp_smart_navwalker.php"; } + + /* Pinegrow generated Include Resources End */ + +/* Creating Editor Blocks with Pinegrow */ + +if ( ! function_exists('leonspharmacy_blocks_init') ) : +function leonspharmacy_blocks_init() { + // Register blocks. Don't edit anything between the following comments. + /* Pinegrow generated Register Pinegrow Blocks Begin */ + require_once 'blocks/contact-block/contact-block_register.php'; + require_once 'blocks/sidebar-heading/sidebar-heading_register.php'; + + /* Pinegrow generated Register Pinegrow Blocks End */ +} +add_action('init', 'leonspharmacy_blocks_init'); +endif; + +/* End of creating Editor Blocks with Pinegrow */ + + +/* Register Blocks Categories */ + +function leonspharmacy_register_blocks_categories( $categories ) { + + // Don't edit anything between the following comments. + /* Pinegrow generated Register Blocks Category Begin */ + +$categories = array_merge( $categories, array( array( + 'slug' => 'Leon Pharmacy Blocks', + 'title' => __( 'Leon Pharmacy Blocks', 'leonpharmacy' ) + ) ) ); + +$categories = array_merge( $categories, array( array( + 'slug' => 'archive', + 'title' => __( 'Archive', 'leonpharmacy' ) + ) ) ); + + /* Pinegrow generated Register Blocks Category End */ + + return $categories; +} +add_action( version_compare('5.8', get_bloginfo('version'), '<=' ) ? 'block_categories_all' : 'block_categories', 'leonspharmacy_register_blocks_categories'); + +/* End of registering Blocks Categories */ + +?> \ No newline at end of file diff --git a/header.php b/header.php new file mode 100644 index 0000000..8845766 --- /dev/null +++ b/header.php @@ -0,0 +1,59 @@ + + +> + + + + + + + + + + + +
\ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..3a98443 Binary files /dev/null and b/icon.png differ diff --git a/inc/allow_svg.php b/inc/allow_svg.php new file mode 100644 index 0000000..da0e06e --- /dev/null +++ b/inc/allow_svg.php @@ -0,0 +1,55 @@ + 'Yoast SEO', + 'slug' => 'wordpress-seo', + 'required' => false, + ) + ); + + /* + * Array of configuration settings. Amend each line as needed. + * + * TGMPA will start providing localized text strings soon. If you already have translations of our standard + * strings available, please help us make TGMPA even better by giving us access to these translations or by + * sending in a pull-request with .po file(s) with the translations. + * + * Only uncomment the strings in the config array if you want to customize the strings. + */ + $config = array( + 'id' => 'tgmpa', // Unique ID for hashing notices for multiple instances of TGMPA. + 'default_path' => '', // Default absolute path to bundled plugins. + 'menu' => 'tgmpa-install-plugins', // Menu slug. + 'parent_slug' => 'themes.php', // Parent menu slug. + 'capability' => 'edit_theme_options', // Capability needed to view plugin install page, should be a capability associated with the parent menu used. + 'has_notices' => true, // Show admin notices or not. + 'dismissable' => true, // If false, a user cannot dismiss the nag message. + 'dismiss_msg' => '', // If 'dismissable' is false, this message will be output at top of nag. + 'is_automatic' => false, // Automatically activate plugins after installation or not. + 'message' => '', // Message to output right before the plugins table. + /* + 'strings' => array( + 'page_title' => __( 'Install Required Plugins', 'theme-slug' ), + 'menu_title' => __( 'Install Plugins', 'theme-slug' ), + // ... + 'nag_type' => 'updated', // Determines admin notice type - can only be 'updated', 'update-nag' or 'error'. + ) + */ + ); + + tgmpa($plugins, $config); +} diff --git a/inc/site_member.php b/inc/site_member.php new file mode 100644 index 0000000..d6ebae7 --- /dev/null +++ b/inc/site_member.php @@ -0,0 +1,114 @@ +roles) == 1 and $ourCurrentUser->roles[0] == 'subscriber') { + wp_redirect(site_url('/')); + exit; + } +} + +// ------------------------------------------------------------------------------------------ + +// Remove Admin Bar For Subs +add_action('wp_loaded', 'noSubsAdminBar'); + +function noSubsAdminBar() +{ + $ourCurrentUser = wp_get_current_user(); + + if (count($ourCurrentUser->roles) == 1 and $ourCurrentUser->roles[0] == 'subscriber') { + show_admin_bar(false); + } +} + +// ------------------------------------------------------------------------------------------ + +// Customize Login Screen +add_filter('login_headerurl', 'ourHeaderUrl'); + +function ourHeaderUrl() +{ + return esc_url(site_url('/')); +} + +add_action('login_enqueue_scripts', 'ourLoginCSS'); + +function ourLoginCSS() +{ + wp_enqueue_style('login styles', get_theme_file_uri('/assets/css/styles.css')); +} + +add_filter('login_headertitle', 'ourLoginTitle'); + +function ourLoginTitle() +{ + return get_bloginfo('name'); +} + +// Logout Without Confirmation +function getLogoutUrl($redirectUrl = '') +{ + if (!$redirectUrl) $redirectUrl = site_url(); + $return = str_replace("&", '&', wp_logout_url($redirectUrl)); + return $return; +} + +// ------------------------------------------------------------------------------------------ + +/** + * Bypass logout confirmation on nonce verification failure + */ +function logout_without_confirmation($action, $result) +{ + if (!$result && ($action == 'log-out')) { + wp_safe_redirect(getLogoutUrl()); + exit(); + } +} +add_action('check_admin_referer', 'logout_without_confirmation', 1, 2); + +// ------------------------------------------------------------------------------------------ + +// Change Registration Message + +function change_reg_message($message) +{ + // change messages that contain 'Register' + if (strpos($message, 'Register') !== FALSE) { + $newMessage = "Register with " . get_bloginfo('name'); + return '

' . $newMessage . '

'; + } else { + return $message; + } +} + +// add our new function to the login_message hook +add_action('login_message', 'change_reg_message'); + + +// ------------------------------------------------------------------------------------------ + +// function custom_wp_new_user_notification_email($wp_new_user_notification_email, $user, $blogname) +// { +// // Customize the email subject +// $wp_new_user_notification_email['subject'] = 'Solr Lights Media'; + +// // Customize the email message +// $wp_new_user_notification_email['message'] = "Dear {$user->display_name}, + +// Thank you for your interest in registering with Solr Lights Media. + +// Please visit the following url to download, fill out and complete the registration form + +// https://google.com"; + +// // Return the modified email +// return $wp_new_user_notification_email; +// } +// add_filter('wp_new_user_notification_email', 'custom_wp_new_user_notification_email', 10, 3); diff --git a/inc/styles_and_scripts.php b/inc/styles_and_scripts.php new file mode 100644 index 0000000..c088770 --- /dev/null +++ b/inc/styles_and_scripts.php @@ -0,0 +1,10 @@ + + + + + + + + + + + + $base_url, + ) + ); + + register_block_type($reg_args['name'], array( + 'render_callback' => empty($reg_args['dynamic']) ? null : function ($attributes, $content, $block) use ($reg_args) { + self::$recursive_level++; + if (self::$recursive_level > 10) { + self::$recursive_level--; + return 'Too many nested blocks... Are you including a post within itself?'; + } + ob_start(); + $args = array('attributes' => $attributes, 'content' => $content, 'block' => $block); + $template = trailingslashit($reg_args[ 'base_path' ]) . $reg_args['render_template']; + if(file_exists( $template )) { + require $template; + } else { + echo "
Dynamic block template $template not found.
"; + } + self::$recursive_level--; + return ob_get_clean(); + }, + 'editor_script' => $editor_script_handle, + 'editor_style' => $editor_style_handle, + 'style' => $style_handle, + 'script' => $script_handle, + 'view_script' => $view_script_handle, + 'attributes' => $reg_args['attributes'], + 'supports' => isset($reg_args['supports']) ? $reg_args['supports'] : array() + ) + ); + } + + static function getDefault($args, $prop, $subprop = null) + { + if (isset($args['block']->block_type->attributes[$prop]['default'])) { + if ($subprop) { + if (isset($args['block']->block_type->attributes[$prop]['default'][$subprop])) { + return $args['block']->block_type->attributes[$prop]['default'][$subprop]; + } else { + return null; + } + } else { + return $args['block']->block_type->attributes[$prop]['default']; + } + } + return null; + } + + static function getAttribute($args, $prop, $use_default = true, $null = false) + { + $val = isset($args['attributes']) && isset($args['attributes'][$prop]) ? $args['attributes'][$prop] : null; + if (($val === null || $val === '') && $use_default) { + $val = self::getDefault($args, $prop); + } + if($val === '' && $null) { + $val = null; + } + return $val; + } + + static function getAttributeForAction($args, $prop, $field = null) + { + $val = self::getAttribute($args, $prop); + if($field) { + if(is_array($val) && isset($val[$field])) { + return $val[$field]; + } else { + return null; + } + } + if(is_array($val)) { + if(isset($val['id'])) return $val['id']; + } + return $val; + } + + static function getAttributeAsPostIds($args, $prop) + { + $r = array(); + $list = self::getAttribute($args, $prop); + if (is_array($list)) { + foreach ($list as $item) { + $r[] = $item['id']; + } + } + if (count($r) === 0) $r[] = 0; + return $r; + } + + static function getInnerContent($args) + { + return isset($args['content']) ? $args['content'] : ''; + } + + static function getImageUrl($args, $prop, $size, $use_default = true) + { + $a = $args['attributes']; + if (!isset($a[$prop])) return $use_default ? self::getDefault($args, $prop, 'url') : null; + if (!empty($a[$prop]['url'])) { + return $a[$prop]['url']; + } + if (empty($a[$prop]['id'])) return $use_default ? self::getDefault($args, $prop, 'url') : null; + if (!empty($a[$prop]['size'])) { + $size = $a[$prop]['size']; + } + return PG_Image::getUrl($a[$prop]['id'], $size); + } + + static function getImageSVG($args, $prop, $use_default = true) + { + return self::getImageField($args, $prop, 'svg', $use_default); + } + + static function getImageField($args, $prop, $field, $use_default = true) + { + $a = $args['attributes']; + if (!isset($a[$prop])) return $use_default ? self::getDefault($args, $prop, $field) : null; + if (!empty($a[$prop][$field])) { + return $a[$prop][$field]; + } + return $use_default ? self::getDefault($args, $prop, $field) : null; + } + + static function getLinkUrl($args, $prop, $use_default = true) + { + $a = self::getAttribute($args, $prop); + if (is_array($a) && isset($a['url'])) { + $val = $a['url']; + } else { + $val = $a; + } + if ($val === null || $val === '') $val = $use_default ? self::getDefault($args, $prop, 'url') : $val; + return $val; + } + + static function mergeInlineSVGAttributes($svg, $props) { + foreach($props as $prop => $val) { + if($prop === 'className') $prop = 'class'; + if(is_array($val)) { + $r = ''; + foreach($val as $key => $v) { + $key = preg_replace_callback("/([A-Z])/g", function($m) { + return '-'.strtolower($m[1]); + }, $key); + $r .= "$key:$v;"; + } + $val = $r; + } + $q = '"'; + if(strpos($val, '"') >= 0) { + $val = str_replace('"', """, $val); + } + $re = "/(]*\\s*)($prop=\"[^\"]*\")/i"; + if(preg_match($re, $svg)) { + $svg = preg_replace($re, '$1' . $prop . '='.$q . $val . $q, $svg); + } else { + $svg = str_replace('setup_postdata( $post ); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/inc/wp_pg_helpers.php b/inc/wp_pg_helpers.php new file mode 100644 index 0000000..9637121 --- /dev/null +++ b/inc/wp_pg_helpers.php @@ -0,0 +1,314 @@ + $list, + 'post_type' => 'attachment', + 'post_mime_type' => 'image', + 'posts_per_page' => -1, + 'orderby' => 'post__in', + 'order' => 'ASC' + ); + return get_posts($args); + } + + static function isPostImage() + { + global $post; + return $post->post_type === 'attachment' && !empty($post->post_mime_type) && strpos($post->post_mime_type, 'image') === 0; + } + + static function getPostImage($id, $size, $args, $remove_sizes = null, $default_img = null) + { + $img = ''; + + if (empty($id) && self::isPostImage()) { + $img = wp_get_attachment_image(get_the_ID(), $size, false, $args); + + } else if (has_post_thumbnail($id)) { + $img = get_the_post_thumbnail($id, $size, $args); + } + + if (!empty($default_image) && empty($img)) { + $img = $default_img; + } + + if (!empty($img) && !empty($remove_sizes)) { + $img = self::removeSizeAttributes($img, $remove_sizes); + } + return $img; + } + + static function getAltText( $id, $default = '') { + $r = trim( wp_strip_all_tags( get_post_meta( $id, '_wp_attachment_image_alt', true ) ) ); + return empty($r) ? $default : $r; + } + } +} + +if(! class_exists( 'PG_Helper_v2' )) { + class PG_Helper_v2 + { + + static function getPostFromSlug($slug_or_id, $post_type) + { + if (is_numeric($slug_or_id)) { + return $slug_or_id; + } + return get_page_by_path($slug_or_id, OBJECT, $post_type); + } + + static function getTermFromSlug($slug_or_id, $taxonomy) + { + if (is_numeric($slug_or_id)) { + return $slug_or_id; + } + switch ($taxonomy) { + case 'category': + return get_category_by_slug($slug_or_id); + default: + return get_term_by('slug', $slug_or_id, $taxonomy); + } + } + + static function getTermIDFromSlug($slug_or_id, $taxonomy, $def = -1) + { + if (is_numeric($slug_or_id)) { + return $slug_or_id; + } + $term = self::getTermFromSlug($slug_or_id, $taxonomy); + return $term ? $term->term_id : $def; + } + + static function addAttributesToElements($tag, $attrs, $html) + { + $attr_str = ''; + + foreach ($attrs as $name => $val) { + $attr_str .= " {$name}"; + if (!is_null($val)) { + $attr_str .= "=\"{$val}\""; + } + } + + if (!empty($attr_str)) { + $html = str_replace("<{$tag} ", "<{$tag}{$attr_str} ", $html); + $html = str_replace("<{$tag}>", "<{$tag}{$attr_str}>", $html); + } + + return $html; + } + + static $shown_posts = array(); + + static function rememberShownPost($p = null) + { + global $post; + if (empty($p)) { + $p = $post; + } + if (!empty($p) && !in_array($p->ID, self::$shown_posts)) { + self::$shown_posts[] = $p->ID; + } + } + + static function getShownPosts() + { + return self::$shown_posts; + } + + static function getInsightMetaFields() + { + $list = array(); + $meta = get_post_meta(get_the_ID()); + if ($meta) { + foreach ($meta as $key => $values) { + if (strpos($key, '_') !== 0) { + $list[] = $key; + } + } + } + echo ''; + } + + static function getRelationshipFieldValue($field) + { + if (function_exists('get_field')) { + return get_field($field, false, false); + } else { + $value = get_post_meta(get_the_ID(), $field); + if (empty($value)) { + return null; + } + if (count($value) === 1) { + if (strpos($value[0], 'a:') >= 0 && strpos($value[0], '{') >= 0) { + return unserialize($value[0]); + } + if (is_string($value[0])) { + return explode(',', $value[0]); + } + } + return $value; + } + } + + static function getPostIdList($value) + { + if (empty($value)) { + return array(-1); + } + if (is_string($value)) { + $value = explode(',', $value); + } + if (is_numeric($value)) { + $value = array($value); + } + if (is_array($value) && count($value) === 0) { + $value = array(-1); + } + return $value; + } + + static function getBreadcrumbs($type = 'parents', $add_home = false, $home_label = '') + { + global $post; + + $r = array(); + + if ($type === 'parents') { + $parents = get_post_ancestors($post->ID); + foreach ($parents as $parent_id) { + $p = get_post($parent_id); + $r[] = array( + 'name' => get_the_title($p), + 'link' => get_permalink($p) + ); + } + } else { + $category = get_the_category($post->ID); + + if (!empty($category)) { + $parents = get_ancestors($category[0]->term_id, 'category'); + + array_unshift($parents, $category[0]->term_id); + + foreach ($parents as $parent_id) { + $p = get_category($parent_id); + $r[] = array( + 'name' => $p->name, + 'link' => get_category_link($p) + ); + } + } + } + + if( is_singular() ) { + array_unshift($r, array( + 'name' => get_the_title($post), + 'link' => get_permalink($post) + )); + } + + if ($add_home) { + $r[] = array( + 'name' => $home_label, + 'link' => home_url() + ); + } + + return array_reverse($r); + } + + static function getArray( $d, $separator = ',' ) { + if(is_array( $d )) return $d; + if(is_null( $d ) || $d === '') return array(); + if(is_string( $d )) { + return array_map('trim', explode( $separator, $d)); + } else { + return array( $d ); + } + } + + static function getTaxonomyQuery( $taxonomy, $terms ) { + $is_or = true; + if(is_string( $terms )) { + if(strpos( $terms, '+') !== false) { + $is_or = false; + } + } + $terms = self::getArray( $terms, $is_or ? ',' : '+'); + if( count( $terms ) === 0 ) return null; + + $field = 'term_id'; + for($i = 0; $i < count( $terms ); $i++) { + if(!is_numeric( $terms[$i] )) { + $field = 'slug'; + break; + } + } + + return array( + 'taxonomy' => $taxonomy, + 'field' => $field, + 'terms' => $terms, + 'include_children' => true, + 'operator' => $is_or ? 'IN' : 'AND' + ); + } + + static function getCurrentPost() { + global $post; + return $post; + } + } + + //Compatibility with any existing custom code + if(! class_exists( 'PG_Helper' )) { + class PG_Helper extends PG_Helper_v2 { + + } + } +} \ No newline at end of file diff --git a/inc/wp_pg_pagination.php b/inc/wp_pg_pagination.php new file mode 100644 index 0000000..d701b9f --- /dev/null +++ b/inc/wp_pg_pagination.php @@ -0,0 +1,72 @@ +max_num_pages; + } + + static function isPaginated( $query = null ) { + return self::getQuery( $query )->max_num_pages > 1; + } + + static function getNextPageUrl( $query = null ) { + $max_pages = self::getMaxPages( $query ); + if(self::getCurrentPage() < $max_pages) { + return get_pagenum_link( self::getCurrentPage() + 1 ); + } + return null; + } + + static function getPreviousPageUrl( $query = null ) { + if(self::getCurrentPage() > 1) { + return get_pagenum_link( self::getCurrentPage() - 1 ); + } + return null; + } + + static function getHrefAttribute( $url ) { + if(empty( $url )) { + return 'href="javascript:void(0)"'; + } else { + return 'href="'.esc_url( $url ).'"'; + } + } + + static function getPreviousHrefAttribute( $query = null ) { + return self::getHrefAttribute( self::getPreviousPageUrl( $query )); + } + + static function getNextHrefAttribute( $query = null ) { + return self::getHrefAttribute( self::getNextPageUrl( $query )); + } +} \ No newline at end of file diff --git a/inc/wp_simple_form_mailer.php b/inc/wp_simple_form_mailer.php new file mode 100644 index 0000000..47f471b --- /dev/null +++ b/inc/wp_simple_form_mailer.php @@ -0,0 +1,239 @@ + 'contact_form', + 'send_to_email' => false, + 'email' => $admin_email, + 'title' => 'Contact form submission', + 'intro' => 'We received a new contact form submission:', + 'save_to_post_type' => null, + 'post_type' => null, + 'captcha' => false, + 'captcha_key' => null, + 'captcha_secret' => null, + 'include_captcha_script' => false, + 'log_ip' => true, + 'success_message' => 'Thank you for getting in touch!', + 'error_message' => 'There was a problem submitting this form. Please contact us directly.', + 'callback' => null + ); + + //merge options + foreach($arg_options as $key => $value) { + $options[ $key ] = $value; + } + + if($options['captcha']) { + if (!empty($options['include_captcha_script']) && !PG_Simple_Form_Mailer::$captcha_script_included) { + echo ''; + PG_Simple_Form_Mailer::$captcha_script_included = true; + } + } + + if( !empty($_POST[$options['form_id']]) ) { + //the form was submitted + //we assume the browser did the validation + $lf = "\n\r"; + + $ignore_fields = array($options['form_id'], 'g-recaptcha-response'); + + $text = $options['intro'].$lf.$lf; + $html = "

{$options['intro']}

"; + + $from_email = null; + + $this->processed = true; + + if($options['captcha']) { + + if(empty($options['captcha_key']) || empty($options['captcha_secret'])) { + $this->error = 'Captcha key and secret are not set.'; + return true; + } + + if(empty($_POST['g-recaptcha-response'])) { + $this->error = 'Captcha response is not present.'; + return true; + + } else if($this->validate_rechapcha($_POST['g-recaptcha-response'], $options['captcha_secret']) !== true) { + $this->error = 'Captcha validation failed.'; + return true; + } + } + + $form_data = array(); + + foreach($_POST as $key => $value) { + if(!in_array( $key, $ignore_fields)) { + $key = htmlspecialchars($key); + $value = htmlspecialchars($value); + + $text .= "{$key}: {$value}".$lf; + + if($key == 'email' || $key == 'Email') { + $from_email = $value; + } + + $html .= "

{$key}: {$value}

"; + + $form_data[$key] = $value; + } + } + + $stamp = "Submitted on ".date("F j, Y, g:i a"); + if($options['log_ip'] && !empty($_SERVER['REMOTE_ADDR'])) { + $stamp .= " from ".$_SERVER['REMOTE_ADDR']; + } + + $text .= $stamp; + $html .= "

{$stamp}

"; + + $this->text = $text; + $this->html = $html; + + $emailed = null; + $saved = null; + $called = null; + + if($options['send_to_email']) { + $headers = 'From: '. $admin_email . "\r\n"; + + $email_data = array( + 'headers' => $headers, + 'subject' => $options['title'], + 'content' => $text, + 'data' => $form_data + ); + + $email_data = apply_filters( 'pg_form_email', $email_data); + + $emailed = wp_mail($options['email'], $email_data['subject'], $email_data['content'], $email_data['headers']); + } + if($options['save_to_post_type']) { + if(wp_insert_post( array( + 'post_title' => $options['title'].(!empty( $from_email ) ? (" - ".$from_email) : ""), + 'post_content' => $html, + 'post_type' => $options['post_type'], + 'post_status' => 'private' + ) )) { + $saved = true; + } else { + $saved = false; + } + } + + if($options['callback']) { + if(function_exists($options['callback'])) { + $called = call_user_func($options['callback'], $form_data, $options); + } else { + $called = false; + } + } + + if((!$emailed && !$saved && !$called) || $emailed === false || $saved === false || $called === false) { + $this->error = true; + $this->message = $options['error_message']; + } else { + $this->error = false; + $this->message = $options['success_message']; + } + + return true; + } else { + //the form was not submitted + $this->processed = false; + $this->error = false; + return false; + } + + } + + //source https://gist.github.com/jonathanstark/dfb30bdfb522318fc819 + public function validate_rechapcha($response, $secret) + { + // Verifying the user's response (https://developers.google.com/recaptcha/docs/verify) + $verifyURL = 'https://www.google.com/recaptcha/api/siteverify'; + + $query_data = [ + 'secret' => $secret, + 'response' => $response, + 'remoteip' => (isset($_SERVER["HTTP_CF_CONNECTING_IP"]) ? $_SERVER["HTTP_CF_CONNECTING_IP"] : $_SERVER['REMOTE_ADDR']) + ]; + + // Collect and build POST data + $post_data = http_build_query($query_data, '', '&'); + + // Send data on the best possible way + if (function_exists('curl_init') && function_exists('curl_setopt') && function_exists('curl_exec')) + { + // Use cURL to get data 10x faster than using file_get_contents or other methods + $ch = curl_init($verifyURL); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-type: application/x-www-form-urlencoded')); + $response = curl_exec($ch); + curl_close($ch); + } + else + { + // If server not have active cURL module, use file_get_contents + $opts = array('http' => + array( + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'content' => $post_data + ) + ); + $context = stream_context_create($opts); + $response = file_get_contents($verifyURL, false, $context); + } + + // Verify all reponses and avoid PHP errors + if ($response) + { + $result = json_decode($response); + if ($result->success === true) + { + return true; + } + else + { + return $result; + } + } + + // Dead end + return false; + } + +} \ No newline at end of file diff --git a/inc/wp_smart_navwalker.php b/inc/wp_smart_navwalker.php new file mode 100644 index 0000000..9800f42 --- /dev/null +++ b/inc/wp_smart_navwalker.php @@ -0,0 +1,279 @@ + 'ul', + 'sub_element' => 'ul', + 'current_class' => 'current-menu-item', + 'sub_menu_class' => 'sub-menu', + 'item_id_prefix' => 'menu-item-', + 'template' => '
  • {LINK_BEFORE}{TITLE}{LINK_AFTER}
  • ', + 'template_item_with_sublevel' => null, + 'template_sublevel' => null, + 'template_subitem' => null + ); + + public static $options = array(); + + private static $level = 0; + private static $current_id = ''; + private static $count = 0; + + public static function init() { + self::$options = self::$default_options; + self::$level = 0; + self::$current_id = ''; + self::$count++; + } + + /** + * Get the begining or end part of the template + * + * @param boolean $start true for begining, false for end part + */ + private function get_template_part( $start = true, $template = 'template' ) { + $parts = explode('{SUB}', self::$options[ $template ]); + if(count($parts) == 2) { + return $start ? $parts[0] : $parts[1]; + } + //if {SUB} is missing from the template, assume the last closing tag is the end part + $idx = strrpos(self::$options[ $template ], ' 0 && self::$options['template_sublevel']) { + $output .= "\n$indent" . str_replace('{ID}', self::$current_id,self::get_template_part(true, 'template_sublevel')); + } else { + $tag = $depth == 0 ? self::$options['top_element'] : self::$options['sub_element']; + if (!empty($tag)) { + $class = self::$options['sub_menu_class']; + $output .= "\n$indent<$tag class=\"$class\">\n"; + } + } + } + + /** + * Ends the list of after the elements are added. + * + * @see Walker::end_lvl() + * + * @since 3.0.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of menu item. Used for padding. + * @param array $args An array of arguments. @see wp_nav_menu() + */ + public function end_lvl( &$output, $depth = 0, $args = array() ) { + $indent = str_repeat("\t", $depth); + + if(self::$level > 0 && self::$options['template_sublevel']) { + $output .= "\n$indent" . self::get_template_part(false, 'template_sublevel'); + } else { + $tag = $depth == 0 ? self::$options['top_element'] : self::$options['sub_element']; + if (!empty($tag)) { + $output .= "$indent\n"; + } + } + self::$level--; + } + + /** + * Start the element output. + * + * @see Walker::start_el() + * + * @since 3.0.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $item Menu item data object. + * @param int $depth Depth of menu item. Used for padding. + * @param array $args An array of arguments. @see wp_nav_menu() + * @param int $id Current item ID. + */ + public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) { + $indent = ( $depth ) ? str_repeat( "\t", $depth ) : ''; + + $classes = empty( $item->classes ) ? array() : (array) $item->classes; + $classes[] = 'menu-item-' . $item->ID; + + /** + * Filter the CSS class(es) applied to a menu item's list item element. + * + * @since 3.0.0 + * @since 4.1.0 The `$depth` parameter was added. + * + * @param array $classes The CSS classes that are applied to the menu item's `
  • ` element. + * @param object $item The current menu item. + * @param array $args An array of {@see wp_nav_menu()} arguments. + * @param int $depth Depth of menu item. Used for padding. + */ + $classes = apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ); + + foreach($classes as $i => $class) { + if($classes[$i] == 'current-menu-item') { + $classes[$i] = self::$options['current_class']; + } + } + $class_names = join( ' ', $classes); + + /** + * Filter the ID applied to a menu item's list item element. + * + * @since 3.0.1 + * @since 4.1.0 The `$depth` parameter was added. + * + * @param string $menu_id The ID that is applied to the menu item's `
  • ` element. + * @param object $item The current menu item. + * @param array $args An array of {@see wp_nav_menu()} arguments. + * @param int $depth Depth of menu item. Used for padding. + */ + $id = apply_filters( 'nav_menu_item_id', self::$options['item_id_prefix']. $item->ID, $item, $args, $depth ); + + if(empty($id)) { + //most likely the default id was already used. Set a count based id + $id = self::$options['item_id_prefix'].self::$count.'-'.$item->ID; + } + + self::$current_id = $id; + + $output .= $indent; + + $atts = array(); + $atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : ''; + $atts['target'] = ! empty( $item->target ) ? $item->target : ''; + $atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : ''; + $atts['href'] = ! empty( $item->url ) ? $item->url : ''; + + /** + * Filter the HTML attributes applied to a menu item's anchor element. + * + * @since 3.6.0 + * @since 4.1.0 The `$depth` parameter was added. + * + * @param array $atts { + * The HTML attributes applied to the menu item's `` element, empty strings are ignored. + * + * @type string $title Title attribute. + * @type string $target Target attribute. + * @type string $rel The rel attribute. + * @type string $href The href attribute. + * } + * @param object $item The current menu item. + * @param array $args An array of {@see wp_nav_menu()} arguments. + * @param int $depth Depth of menu item. Used for padding. + */ + $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth ); + + $attributes = ''; + foreach ( $atts as $attr => $value ) { + if ( ! empty( $value ) ) { + $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value ); + $attributes .= ' ' . $attr . '="' . $value . '"'; + } + } + + $item_output = $args->before; + + $has_sublevel = false; + + if(isset($item->classes) && in_array('menu-item-has-children', $item->classes)) { + $has_sublevel = true; + } + + $template_key = 'template'; + if(self::$level === 0 && $has_sublevel && self::$options['template_item_with_sublevel']) { + $template_key = 'template_item_with_sublevel'; + } else if(self::$level > 0 && self::$options['template_subitem']) { + $template_key = 'template_subitem'; + } + + $template = $this->get_template_part(true, $template_key); + $template = str_replace( '{LINK_BEFORE}', $args->link_before, $template); + $template = str_replace( '{LINK_AFTER}', $args->link_after, $template); + $template = str_replace( '{TITLE}', apply_filters( 'the_title', $item->title, $item->ID ), $template); + + $template = str_replace( '{ATTRS}', $attributes, $template); + $template = str_replace( '{ID}', $id, $template); + $template = str_replace( '{CLASSES}', $class_names, $template); + + $item_output .= $template; + + $item_output .= $args->after; + + /** + * Filter a menu item's starting output. + * + * The menu item's starting output only includes `$args->before`, the opening ``, + * the menu item's title, the closing ``, and `$args->after`. Currently, there is + * no filter for modifying the opening and closing `
  • ` for a menu item. + * + * @since 3.0.0 + * + * @param string $item_output The menu item's starting HTML output. + * @param object $item Menu item data object. + * @param int $depth Depth of menu item. Used for padding. + * @param array $args An array of {@see wp_nav_menu()} arguments. + */ + $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args ); + } + + /** + * Ends the element output, if needed. + * + * @see Walker::end_el() + * + * @since 3.0.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $item Page data object. Not used. + * @param int $depth Depth of page. Not Used. + * @param array $args An array of arguments. @see wp_nav_menu() + */ + public function end_el( &$output, $item, $depth = 0, $args = array() ) { + $has_sublevel = false; + + if(isset($item->classes) && in_array('menu-item-has-children', $item->classes)) { + $has_sublevel = true; + } + + $template_key = 'template'; + if(self::$level === 0 && $has_sublevel && self::$options['template_item_with_sublevel']) { + $template_key = 'template_item_with_sublevel'; + } else if(self::$level > 0 && self::$options['template_subitem']) { + $template_key = 'template_subitem'; + } + $output .= $this->get_template_part(false, $template_key)."\n"; //get the ending part of the template + } + +} // PG_Smart_Walker_Nav_Menu \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..9027a62 --- /dev/null +++ b/index.php @@ -0,0 +1,7 @@ + + +
    + +
    + + \ No newline at end of file diff --git a/page-awards.php b/page-awards.php new file mode 100644 index 0000000..8ce62f6 --- /dev/null +++ b/page-awards.php @@ -0,0 +1,139 @@ + + + +

    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    +
    + +
    +
    +

    +

    +
    +
    +
    + + \ No newline at end of file diff --git a/page-blisterpack.php b/page-blisterpack.php new file mode 100644 index 0000000..f089996 --- /dev/null +++ b/page-blisterpack.php @@ -0,0 +1,73 @@ + + + +

    +
    +
    + +
    +

    +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    +
    +
    +
    +
    +
    +

    +
    +
    +

    .

    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +

    +
    +

    +
    +
    + + \ No newline at end of file diff --git a/page-services.php b/page-services.php new file mode 100644 index 0000000..bc4e93f --- /dev/null +++ b/page-services.php @@ -0,0 +1,126 @@ + + +
    + +
    +
    +
    +
    +

    +
    +

    +
    + +
    +
    +
    +

    +
    +

    +

    +

    + +
    + +
    +
    +
    +

    +
    +

    +

    +

    +
    + +
    +
    +
    +

    +
    +

    +

    +

    +
    + +
    +
    +
    +

    +

    +
    +

    +

    +

    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    +
    +

    +
    +

    +

    +

    +
    + +
    +
    + + \ No newline at end of file diff --git a/page.php b/page.php new file mode 100644 index 0000000..7781197 --- /dev/null +++ b/page.php @@ -0,0 +1,14 @@ + + + +
    + +
    + + \ No newline at end of file diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..d1f3b3d Binary files /dev/null and b/screenshot.png differ diff --git a/single.php b/single.php new file mode 100644 index 0000000..314b266 --- /dev/null +++ b/single.php @@ -0,0 +1,62 @@ + + +
    +
    +
    +
    + + + + +

    + + + $term ) : ?> + + name; ?> + + + + + + +

    +
    +
    + 'max-h-full object-center object-cover', + 'sizes' => '(max-width: 768px) 100vw, (max-width: 1024px) 22vw, (max-width: 1280px) 29vw, 370px' + ), 'both', null ) ?> +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    + +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..947e90a --- /dev/null +++ b/style.css @@ -0,0 +1,11 @@ +/* +Theme Name: Leon Pharmacy +Theme URI: https://harpLabs.com +Author: Prospect Ogujiuba +Author URI: https://thecreatorpriz.com +Description: This theme was created for Leon Pharmacy Medical Clinic and Pharmacy by Harplabs. +Version: 1.5 +License: GNU General Public License v2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html +Text Domain: HarpLabs +*/ \ No newline at end of file diff --git a/theme.json b/theme.json new file mode 100644 index 0000000..f439bbb --- /dev/null +++ b/theme.json @@ -0,0 +1,459 @@ +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 2, + "settings": { + "color": { + "palette": [ + { + "color": "white", + "name": "current", + "slug": "current" + }, + { + "color": "rgba(0,0,0,0)", + "name": "transparent", + "slug": "transparent" + }, + { + "color": "black", + "name": "black", + "slug": "black" + }, + { + "color": "white", + "name": "white", + "slug": "white" + }, + { + "color": "rgba(0,0,0,0.5)", + "name": "inherit", + "slug": "inherit" + }, + { + "color": "rgba(249,250,251,1)", + "name": "gray-50", + "slug": "gray-50" + }, + { + "color": "rgba(243,244,246,1)", + "name": "gray-100", + "slug": "gray-100" + }, + { + "color": "rgba(229,231,235,1)", + "name": "gray-200", + "slug": "gray-200" + }, + { + "color": "rgba(209,213,219,1)", + "name": "gray-300", + "slug": "gray-300" + }, + { + "color": "rgba(156,163,175,1)", + "name": "gray-400", + "slug": "gray-400" + }, + { + "color": "rgba(107,114,128,1)", + "name": "gray-500", + "slug": "gray-500" + }, + { + "color": "rgba(75,85,99,1)", + "name": "gray-600", + "slug": "gray-600" + }, + { + "color": "rgba(55,65,81,1)", + "name": "gray-700", + "slug": "gray-700" + }, + { + "color": "rgba(31,41,55,1)", + "name": "gray-800", + "slug": "gray-800" + }, + { + "color": "rgba(17,24,39,1)", + "name": "gray-900", + "slug": "gray-900" + }, + { + "color": "#f6d9d9", + "name": "red-50", + "slug": "red-50" + }, + { + "color": "#f3b5ab", + "name": "red-100", + "slug": "red-100" + }, + { + "color": "#eb9280", + "name": "red-200", + "slug": "red-200" + }, + { + "color": "#e06e56", + "name": "red-300", + "slug": "red-300" + }, + { + "color": "#d2462d", + "name": "red-400", + "slug": "red-400" + }, + { + "color": "#c00000", + "name": "red-500", + "slug": "red-500" + }, + { + "color": "#a50004", + "name": "red-600", + "slug": "red-600" + }, + { + "color": "#8b0005", + "name": "red-700", + "slug": "red-700" + }, + { + "color": "#720005", + "name": "red-800", + "slug": "red-800" + }, + { + "color": "#5a0003", + "name": "red-900", + "slug": "red-900" + }, + { + "color": "#430000", + "name": "red-950", + "slug": "red-950" + }, + { + "color": "#fef7dc", + "name": "yellow-50", + "slug": "yellow-50" + }, + { + "color": "#ffefbc", + "name": "yellow-100", + "slug": "yellow-100" + }, + { + "color": "#ffe69b", + "name": "yellow-200", + "slug": "yellow-200" + }, + { + "color": "#ffdd77", + "name": "yellow-300", + "slug": "yellow-300" + }, + { + "color": "#fed44e", + "name": "yellow-400", + "slug": "yellow-400" + }, + { + "color": "#facc15", + "name": "yellow-500", + "slug": "yellow-500" + }, + { + "color": "#d7b013", + "name": "yellow-600", + "slug": "yellow-600" + }, + { + "color": "#b59410", + "name": "yellow-700", + "slug": "yellow-700" + }, + { + "color": "#95790d", + "name": "yellow-800", + "slug": "yellow-800" + }, + { + "color": "#75600a", + "name": "yellow-900", + "slug": "yellow-900" + }, + { + "color": "#574707", + "name": "yellow-950", + "slug": "yellow-950" + }, + { + "color": "rgba(236,253,245,1)", + "name": "green-50", + "slug": "green-50" + }, + { + "color": "rgba(209,250,229,1)", + "name": "green-100", + "slug": "green-100" + }, + { + "color": "rgba(167,243,208,1)", + "name": "green-200", + "slug": "green-200" + }, + { + "color": "rgba(110,231,183,1)", + "name": "green-300", + "slug": "green-300" + }, + { + "color": "rgba(52,211,153,1)", + "name": "green-400", + "slug": "green-400" + }, + { + "color": "rgba(16,185,129,1)", + "name": "green-500", + "slug": "green-500" + }, + { + "color": "rgba(5,150,105,1)", + "name": "green-600", + "slug": "green-600" + }, + { + "color": "rgba(4,120,87,1)", + "name": "green-700", + "slug": "green-700" + }, + { + "color": "rgba(6,95,70,1)", + "name": "green-800", + "slug": "green-800" + }, + { + "color": "rgba(6,78,59,1)", + "name": "green-900", + "slug": "green-900" + }, + { + "color": "#dadfe3", + "name": "blue-50", + "slug": "blue-50" + }, + { + "color": "#afb6c1", + "name": "blue-100", + "slug": "blue-100" + }, + { + "color": "#858fa0", + "name": "blue-200", + "slug": "blue-200" + }, + { + "color": "#5d6a80", + "name": "blue-300", + "slug": "blue-300" + }, + { + "color": "#364761", + "name": "blue-400", + "slug": "blue-400" + }, + { + "color": "#0a2744", + "name": "blue-500", + "slug": "blue-500" + }, + { + "color": "#0b2138", + "name": "blue-600", + "slug": "blue-600" + }, + { + "color": "#0b1b2d", + "name": "blue-700", + "slug": "blue-700" + }, + { + "color": "#091522", + "name": "blue-800", + "slug": "blue-800" + }, + { + "color": "#040e18", + "name": "blue-900", + "slug": "blue-900" + }, + { + "color": "rgba(238,242,255,1)", + "name": "indigo-50", + "slug": "indigo-50" + }, + { + "color": "rgba(224,231,255,1)", + "name": "indigo-100", + "slug": "indigo-100" + }, + { + "color": "rgba(199,210,254,1)", + "name": "indigo-200", + "slug": "indigo-200" + }, + { + "color": "rgba(165,180,252,1)", + "name": "indigo-300", + "slug": "indigo-300" + }, + { + "color": "rgba(129,140,248,1)", + "name": "indigo-400", + "slug": "indigo-400" + }, + { + "color": "rgba(99,102,241,1)", + "name": "indigo-500", + "slug": "indigo-500" + }, + { + "color": "rgba(79,70,229,1)", + "name": "indigo-600", + "slug": "indigo-600" + }, + { + "color": "rgba(67,56,202,1)", + "name": "indigo-700", + "slug": "indigo-700" + }, + { + "color": "rgba(55,48,163,1)", + "name": "indigo-800", + "slug": "indigo-800" + }, + { + "color": "rgba(49,46,129,1)", + "name": "indigo-900", + "slug": "indigo-900" + }, + { + "color": "#f8ebf3", + "name": "purple-50", + "slug": "purple-50" + }, + { + "color": "#f3d7ea", + "name": "purple-100", + "slug": "purple-100" + }, + { + "color": "#eab7d9", + "name": "purple-200", + "slug": "purple-200" + }, + { + "color": "#dc8abe", + "name": "purple-300", + "slug": "purple-300" + }, + { + "color": "#ce66a4", + "name": "purple-400", + "slug": "purple-400" + }, + { + "color": "#ba4887", + "name": "purple-500", + "slug": "purple-500" + }, + { + "color": "#a0366d", + "name": "purple-600", + "slug": "purple-600" + }, + { + "color": "#84305b", + "name": "purple-700", + "slug": "purple-700" + }, + { + "color": "#6f2c4e", + "name": "purple-800", + "slug": "purple-800" + }, + { + "color": "#43142c", + "name": "purple-900", + "slug": "purple-900" + }, + { + "color": "rgba(253,242,248,1)", + "name": "pink-50", + "slug": "pink-50" + }, + { + "color": "rgba(252,231,243,1)", + "name": "pink-100", + "slug": "pink-100" + }, + { + "color": "rgba(251,207,232,1)", + "name": "pink-200", + "slug": "pink-200" + }, + { + "color": "rgba(249,168,212,1)", + "name": "pink-300", + "slug": "pink-300" + }, + { + "color": "rgba(244,114,182,1)", + "name": "pink-400", + "slug": "pink-400" + }, + { + "color": "rgba(236,72,153,1)", + "name": "pink-500", + "slug": "pink-500" + }, + { + "color": "rgba(219,39,119,1)", + "name": "pink-600", + "slug": "pink-600" + }, + { + "color": "rgba(190,24,93,1)", + "name": "pink-700", + "slug": "pink-700" + }, + { + "color": "rgba(157,23,77,1)", + "name": "pink-800", + "slug": "pink-800" + }, + { + "color": "rgba(131,24,67,1)", + "name": "pink-900", + "slug": "pink-900" + } + ] + }, + "typography": { + "fontFamilies": [ + { + "fontFamily": "'Montserrat', sans-serif", + "name": "Montserrat", + "slug": "montserrat" + }, + { + "fontFamily": "'Inter', sans-serif", + "name": "Inter", + "slug": "inter" + } + ] + } + } +} \ No newline at end of file
  • +
    ', + esc_html__('Upgrade message from the plugin author:', 'tgmpa'), + ' ', wp_kses_data($item['upgrade_notice']), ' +
    +